[toc]
前言
如何学习设计模式
- 这个设计模式的意图是什么?
- 解决一个什么问题,什么时候可以使用它,怎么解决的?
- 结构图、关键代码、应用实例(生活中,软件中)。
- 优缺点,使用时要注意什么?
关于可维护、可复用、可扩展、灵活性好的理解:
生活中:印刷术和活字印刷,当需要对某些内容修改时,印刷术只要有一丁点变化,就需要重头再来;而活字印刷只需要进行部分修改即可。
可维护:只更改要更改的内容;
可复用:之前的内容并非用完就无用,后面仍可使用;
可扩展:若要新增内容,只需在原来内容的基础上增加新内容即可;
灵活性:内容可以稍作修改既可以满足要求。
封装、继承、多态把程序的耦合度降低,使用设计模式使得程序更加灵活,容易修改并易于复用。
UML类图

(图片来自大话数据结构)
类
类图分为三层,第一层显示类的名称,如果是抽象类,则用斜体显示;第二层是类的特性,通常是字段和属性;第三层是类的操作,通常是方法或行为;注意前面的符号,“+”:public,“-”:private,“#”:protected。
接口
与类图的区别主要是顶端有<
>显示;第一行是接口名称,第二行是接口方法。接口还有棒棒糖表示法。 继承(类与类)
空心三角形+实线;空心三角形在被继承端
实现(类与接口)
空心三角形+虚线;空心三角形在被实现端
关联
当一个类需要知道另一个类时,如企鹅需要知道气候变化,需要了解气候规律。用实线箭头表示,箭头指向被了解方
聚合
比如:每只大雁都属于一个雁群,而一个雁群可以有多只大雁;它们之间就满足聚合关系。聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。聚合用空心菱形+实线箭头表示,菱形在B对象上,箭头指向A对象
合成,组合
比如:鸟和翅膀就是组合关系,是一种强的‘拥有’关系,体现严格的部分和整体的关系,两者生命周期一样。合成关系用实心菱形+实线箭头来表示,实心菱形在拥有端,箭头指向被拥有端,在合成关系的连线两端还有数字,称为基数,表示这一段的类可以有几个实例,
依赖
用虚线箭头表示,箭头指向被依赖端
设计模式六大原则
单一职责原则:就一个类而言, 应该仅有一个引起它变化的原因
开放-封闭原则:类、模块、函数等应该是可以拓展的,但是不可修改
依赖倒转原则: 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
里式替换原则: 所有引用基类的地方必须能透明地使用其子类的对象 、
接口隔离原则: 一个类对另一个类的依赖应该建立在最小的接口上
迪米特原则: 一个软件实体应当尽可能少地与其他实体发生相互作用 ,使得系统功能模块相对独立
常见设计模式
简单工厂模式
解决如何去实例化对象,到底要实例化谁,将来会不会增加实例化;考虑用一个单独的类完成创造实例的过程。
例子:实现一个计算器功能

若添加一个开平方根运算,只需要新建一个类继承运算类,同时在简单工厂类里面添加实例化开平方根的类对象即可。
单例模式
要么不要有,有只能一个;保证一个类仅有一个实例,并提供一个访问它的全局访问点。让类自身负责唯一实例。这个类保证没有其他实例被创建,但可以提供一个访问该实例的方法

单例模式除了可以保证唯一的实例外,还可以严格控制客户怎样访问它以及何时访问它,简单说就是对唯一实例额的受控访问。
多线程时的单例:
多个线程同时访问Singleton类,调用GetInstance()方法,会可能造成创建多个实例;这个时候就需要一把锁来处理,
静态初始化方式不同
懒汉式:面临多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全,
public class Singleton{
//懒汉模式
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null ){
instance = new Singleton();
}
return instance;
}
}
双重检验确保线程安全
public class Singleton{
private static volatile Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
//不必让线程每次都加锁,只在实例未被创建的时候再加锁处理
if(instance == null ){
//这里确保只有一个线程可以进入
synchronized (Singleton.class){
//确保该实例没有被其他线程实例化过
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
饿汉式:类一加载就实例化的对象,需要提前占用系统资源
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
模板方法
定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

//题外话:可以看出抽象类中可以有非抽象方法
public abstract class AbstractParent {
//模板方法,给出逻辑的骨架,而逻辑的组成是一些相对应的抽象对象,他们推迟到子类实现
public void templateMethod(){
dressUp();
eatBreakFast();
doSomeThing();
}
//一些抽象方法,放到子类中去实现
public abstract void dressUp();
public abstract void eatBreakFast();
public abstract void doSomeThing();
}
//子类分别给出这些抽象方法的不同实现,从而使得模板方法的实现各有不同
public class ChildrenA extends AbstractParent {
@Override
public void dressUp() {
}
@Override
public void eatBreakFast() {
}
@Override
public void doSomeThing() {
}
}
//客户端调用
public static void main(String[] args){
AbstractParent c;
c = new ChildrenA();
c.TemplateMethod();
c = new ChildrenB();
c.TemplateMethod();
}
优势:模板方法是将不变行为搬移到超类,去除子类中重复的代码
应用:namenode在INode类及其子类INodeWithAdditionalFields中采用模板设计模式
职责链模式
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递下去,直到有一个对象处理它为止

应用:HDFS源码
观察者模式
观察者模式又叫发布-订阅模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

应用:ZooKeeper的watcher机制