14 KiB
快速理解设计模式
设计模式主要分为3大类,分别是创建型模式、行为型模式和结构型模式。
UML类图术语
类的属性的表示方式
· + :表示public
· - :表示private
· #:表示protected(friendly也归入这类)
类的方法的表示方式
类和类之间关系的表示方式
1、依赖关系
一个类的某个方法引用到了另外一个类,即一个类的调用依赖于另外一个类的定义。
UML表示:采用虚线+箭头的方式表示依赖关系
2、关联关系
体现的是两个类或者类和接口之间语义级别的强依赖关系,它使得一个类知道另外一个类的属性和方法,通常是一个类拥有另外一个类或者接口的成员变量。
UML类图:使用普通箭头表示
3、聚合关系
一种强的关联关系,聚合是整体和个体之间的关系,两个类处于不平等的层次上,一个代表整体,一个代表局部。体现的是has-a的关系。
UML表示:使用空心菱形箭头表示。
4、组合关系
组合关系也是关联关系的一种,比聚合关系更强的关系。组合关系体现的是一种contains-a的关系,同样体现的是整体和局部的关系,但是整体和局部是不可分的,整体的生命周期结束也就意味着部分的生命周期的结束。
UML表示:使用实心菱形箭头表示。
5、实现关系
指的是类和接口之间的关系,一个类实现另外一个接口,实现接口的方法,添加自己的新功能。
UML表示:使用虚线+三角形表示
6、继承关系
指的是类和类之间、接口和接口之间的关系。子类全部继承父类的方法和属性。
UML表示:使用实线+三角形表示
创建型模式
创建型模式主要是和创建对象相关的一些设计模式
单例模式
目的:确保某个类在jvm中只有一个实例,提供该实例的全局访问点。
类图:使用一个私有构造函数、私有静态变量,和一个共有的静态函数来实现
懒汉式(线程不安全)、饿汉式、懒汉式(线程安全)
双重校验锁(注意volatile):
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
为什么要使用volatile? uniqueInstance = new Singleton(); 这段代码其实是分三步执行:
- 分配内存空间
- 初始化对象
- 将 uniqueInstance 指向分配的内存地址
JVM的指令重排序可能导致执行顺序编程1-3-2, 在多线程情况下,可能获取一个没有初始化的实例,导致程序出错。
使用volatile 可以禁止JVM的指令重排序,保证多线程的环境下程序的正常运行。
静态内部类:
实现延迟加载,也能实现一次加载,线程安全、相当于同步的懒汉式
枚举类型:单例模式的最佳实践,实现简单,并且能在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
简单工厂模式
目的:创建对象的时候隐藏内部细节,提供一个创建对象的通用接口。
类图:在简单工厂模式中,根据传入type类型来决定到底生产哪种具体的产品类
工厂方法
目的:工厂方法把类的实例化操作放到子类来执行,本身抽象化。
类图:在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
抽象工厂模式
目的: 抽象工厂模式使用抽象工厂类来产生多个具体的工厂,由具体的工厂来生产各自的产品,最终关联到抽象产品类中,client通过抽象工厂来生产具体的产品。
建造者(生成器)模式
目的:封装一个对象的构造过程,允许按步骤构造
类图:在建造者模式中,通过抽象Builder对象来一步步建造
例如JDK中的StringBuilder就是采用建造者模式实现的。
原型模式
目的:使用原型实例指定要创建对象的类型,然后通过复制这个原型来创建新对象,即通过原型模式创建复杂对象
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
java.lang.Object#clone()采用的就是原型模式
行为型模式
行为型设计模式主要是表示类和对象如何交互,以及如何划分责任和算法。
责任链模式
目的:将请求的发送者和接收者解耦,使得多个对象都有处理这个请求的机会。
类图:定义一个抽象Handler类,实现具体的Handler类来处理请求。
JDK
java.util.logging.Logger#log() Apache Commons Chain javax.servlet.Filter#doFilter()
命令模式
目的:将命令封装成对象中,以便使用命令来参数化其它对象,或者将命令对象放入队列中进行排队,或者将命令对象的操作记录到日志中,以及支持可撤销的操作。
类图:
JDK java.lang.Runnable Netflix Hystrix javax.swing.Action
解释器模式
目的:创建语言的解释器,通常有语言的语法和语法分析来定义
JDK java.util.Pattern java.text.Normalizer All subclasses of java.text.Format javax.el.ELResolver
迭代器模式
目的:提供一种顺序访问对象元素的方法,并且不暴露聚合对象的内部表示
中介者模式
目的:集中相关对象之间复杂的沟通和控制方式
类图:
- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
- Colleague:同事,相关对象
备忘录模式
目的:在不违反封装的情况下获得对象的内部状态,从而在需要的时候可以将对象恢复到最初状态。
类图:
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。
观察者模式
目的:定义对象之间的一对多依赖,当一个对象状态改变时,它所有依赖都会受到通知并且自动更新状态。
类图:
JDK java.util.Observer java.util.EventListener javax.servlet.http.HttpSessionBindingListener RxJava
状态模式
目的:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
类图:
策略模式
目的:定义一些列算法,封装每个算法,并使他们可以互换。策略模式可以让算法独立于使用它的客户端。
类图:
- Strategy 接口定义了一个算法族,它们都具有 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(in Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
与状态模式的比较 状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
模板方法
目的:定义算法框架,并将一些步骤的实现延迟到子类。通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
类图:
访问者模式
目的:为一个对象结构(比如组合结构)增加新能力。
类图:
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。
结构型
结构型模式是用于类或者对象之间的组合关系。
适配器模式
目的:把一个类的接口转换成另外一个用户需要的接口
类图:
桥接模式
目的:将抽象和实现分离,可以独立变化
类图:
- Abstraction: 定义抽象类的接口
- Implementor: 定义实现类的接口
组合模式
目的:将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
类图:
JDK:
List Map
装饰者模式
目的:为对象动态添加功能
类图:装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。
外观模式
目的:提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
类图:
享元模式
目的:利用共享的方式来支持大量细粒度的对象,这些对象的一部分内部状态是相同的。
类图:
- Flyweight:享元对象
- IntrinsicState:内部状态,享元对象共享内部状态
- ExtrinsicState:外部状态,每个享元对象的外部状态不同
代理模式
目的:控制对其他对象的访问。
类图:
代理模式和装饰者模式的区别 从功能上看,代理模式实现被代理类的简介访问,装饰者模式则是动态添加类的功能 从类图上看,代理类和装饰类均与目标类实现了同一接口,但是代理类拥有真实目标类的成员(关联关系)