目录
[TOC]
23种设计模式
GOF
在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。
- 对接口编程而不是对实现编程。
- 优先使用对象组合而不是继承。
设计模式简介
| 模式类型 | 模式 & 描述 | 包括 |
|---|---|---|
| 创建型模式 | 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式, 而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 |
单例模式(Singleton Pattern) 原型模式(Prototype Pattern) 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 建造者模式(Builder Pattern) |
| 结构型模式 | 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 代理模式(Proxy Pattern) 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) |
| 行为型模式 | 这些设计模式特别关注对象之间的通信。 | 模板模式(Template Pattern) 策略模式(Strategy Pattern) 命令模式(Command Pattern) 责任链模式(Chain of Responsibility Pattern) 状态模式(State Pattern) 观察者模式(Observer Pattern) 中介者模式(Mediator Pattern) 迭代器模式(Iterator Pattern) 访问者模式(Visitor Pattern) 备忘录模式(Memento Pattern) 解释器模式(Interpreter Pattern) |
| J2EE 模式 | 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern) 业务代表模式(Business Delegate Pattern) 组合实体模式(Composite Entity Pattern) 数据访问对象模式(Data Access Object Pattern) 前端控制器模式(Front Controller Pattern) 拦截过滤器模式(Intercepting Filter Pattern) 服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern) |
设计模式七大原则
单一职责原则
定义
一个类应该只负责一项职责
目的
- 降低类的复杂度
- 提高类的可读性,可维护性
接口隔离原则
定义
一个类对另一个类的依赖应该建立在最小接口上。
目的
降低类之间的耦合度
实现
尽可能对接口进行拆分
依赖倒转原则
定义
面向接口编程,针对接口编程,依赖于抽象而不依赖于具体。
目的
编程时,制定好规范,而不涉及具体实现操作,增加代码通用性和稳定性。
实现
利用了多态性
里式替换原则
定义
继承必须确保超类所拥有的性质在子类中仍然成立。里式替换原则说明继承实际上让两个类的耦合程度增加,在适当情况下,应该使用聚合,组合,依赖等形式组织类之间的关系。
目的
- 类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性
- 里氏替换原则是实现开闭原则的重要方式之一
实现
子类尽量不要重写父类中的方法
开闭原则
定义
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
目的
- 提高代码复用能力
- 提高软件可维护性,方便进行软件测试。后期扩展只需要测试新添加部分,因为原有代码未受到影响
实现
使用接口和抽象类
迪米特原则
定义
又称最小知道原则,一个类只与直接朋友通信,即一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
直接朋友:成员变量,方法参数,方法返回值中的类。局部变量中的类不是直接朋友
目的
- 减少类之间的耦合度,提高模块独立性
- 提高类的可复用率和系统的扩展性
实现
使用中介类来解耦
合成复用原则
定义
通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用
继承复用破坏了类的封装性,将父类的实现细节暴露给子类,也称“白箱复用”。
目的
- 维持类的封装性,合成复用也称“黑箱复用”
- 降低耦合度
- 复用更加灵活,针对接口进行编程。
实现
尽量使用合成/聚合的方式,而不是使用继承
示例
创建型模式(5种)
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。
单例模式
一个类只有一个实例,且该类能自行创建这个实例的一种模式。
作用
节省内存资源、保证数据内容的一致性
示例
静态内部类式单例
1 | public class SingleTon{ |
枚举单例
- 反序列化后生成对象和原来对象是同一个对象
- 避免反射攻击(因为枚举类的构造方法继承自Enum类,在newInstance时,会检查当前类是不是Enum类,如果是则抛出异常)
1 | enum Singleton implements xx{ |
原型模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象,即克隆。
作用
在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效
浅度克隆
定义
只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。
示例
Java中clone函数就是一个浅度克隆
1 | public class Monkey implements Cloneable { |

深度克隆
定义
既克隆的值,还克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。
换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
注意
- 深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理
- ==一般使用序列化和反序列机制来实现深度克隆==
- 对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。
- 有一些对象,比如线程(Thread)对象或Socket对象,是不能简单复制或共享的。不管是使用浅度克隆还是深度克隆,只要涉及这样的间接对象,就必须把间接对象设成transient而不予复制;或者由程序自行创建出相当的同种对象,权且当做复制件使用
示例
1 | //实现序列化接口 |

带原型管理器的原型模式
在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型(实现同一接口的),Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。
1 | interface Shape extends Cloneable |
工厂模式
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。我们把被创建的对象称为产品,把创建产品的对象称为工厂
工厂中也可以使用一个Map来缓存产品,下次创建直接从Map中获取,这就成了享元模式。
工厂模式分类
简单工厂模式:多个产品实现相同接口,工厂根据id生产具体类型的产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}工厂方法模式:多个产品实现相同接口,工厂中不同方法生产不同的产品
1
2
3
4
5
6
7
8public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
和工厂模式不同之处
工厂模式:仅仅针对产品的生成
抽象工厂模式:针对产品族中的产品,即选择哪个工厂,就只能使用该工厂提供的产品
进一步说明:车的种类有汽车、摩托车、货车,飞机的种类有客机,战斗机,车和飞机的品牌有奔驰、宝马。
工厂模式只能选择车的种类
抽象工厂模式先选择车的品牌(生产商),然后再选择车的种类
1 | // ------------------------------产品1 |
建造者模式
建造者模式是用于构建复杂对象,且复杂对象通常由多个子部件按一定的步骤组合而成
建造者模式的主要角色
- 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个部件(实际产品)。
- 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回产品的方法 getResult()。
- 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息(实现构造复杂对象时的逻辑)。
1 | //产品 --------------------------- |
改进的建造者模式
为了简化系统结构,可以把指挥者和抽象建造者进行结合
==这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果construct() 过于复杂,建议还是封装到 Director==
1 | interface class Builder { |
重构传统实例化方式
对于一些子部件不需要特定步骤组合但是传入参数很多的复杂对象构建,传统方式构建可读性很差,可以使用builder模式重构
1 | //产品 --------------------------- |
结构型模式(7种)
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
代理模式
访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。同时,代理方式还可以用来对代码前后添加一些逻辑处理
静态代理
动态代理
利用JDK动态代理类通过接口创建代理类
1 | //接口 |
动态代理实际上也是由字节码来生成一个代理类(保存在内存中)
==public final class $Proxy0 extends Proxy implements Subject 通过生成的代理类对象可以看出,该类已经继承了JDK的Proxy类,所以不能再继承其他类,所以必须通过实现接口来完成代理==
1 | class Proxy{ |
Cglib代理
通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。因为采用的是继承,所以不能对final修饰的类进行代理。
cglib 是基于asm 字节修改技术。导入 cglib 会间接导入 asm, ant, ant-launcher 三个jar 包。
Spring框架的spring-core.jar包中已经集成了cglib与asm。
1 | <!-- cglib 动态代理依赖 begin --> |
1 | //被代理对象 |
1 | //真正生成代理类 |
为什么intercept中不能使用method.invoke(obj,args)
这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, …)很明显会死循环。
使用proxy.invokeSuper(obj,args)原理
cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。
fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。
动态代理和Cglib的区别
- jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。
- cglib执行速度略大于jdk,所以比较适合单例模式。
- 由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。
适配器模式
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分类
类结构型模式,java没有多继承,所以只能继承和实现结合使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package adapter;
//目标接口
interface Target
{
public void request();
}
//适配者接口
class Adaptee
{
public void specificRequest()
{
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
public void request()
{
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest
{
public static void main(String[] args)
{
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}对象结构型模式,推荐使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package adapter;
//对象适配器类
class ObjectAdapter implements Target
{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee)
{
this.adaptee=adaptee;
}
public void request()
{
adaptee.specificRequest();
}
}
//客户端代码
public class ObjectAdapterTest
{
public static void main(String[] args)
{
System.out.println("对象适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
桥接模式
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
当某个类存在多个维度变化时,如果全部使用继承,则会有n*m种子类,使用组合方式来实现,则只会有n+m个子类
示例
包按用途分为钱包(Wallet)和挎包(HandBag),按颜色分为黄色(Yellow)和红色(Red)
装饰模式
在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。(感觉和静态代理模式很像)
外观模式
一种通过为多个复杂的子系统提供一个统一的接口,而使这些子系统更加容易被访问的模式。

优点
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
- 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象
缺点
- 不能很好地限制客户使用子系统类。
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
示例
享元模式
通过缓存对象来大幅度减少需要创建的对象数量,从而提高系统资源的利用率。
享元模式的主要角色
抽象享元角色(Flyweight)
是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
具体享元(Concrete Flyweight)角色
可共享的角色,需实现抽象享元角色中所规定的接口。
==如果只有一个种类,但是由于参数不同而产生不同对象的享元角色,其实并不需要构建抽象享元角色,如java基本数据类型包装类的缓存,只是值不同,就不需要构建抽象类;还有连接池和线程池==
- Byte, Short, Long 缓存的范围都是 -128~127
- Character 缓存的范围是 0~127
- Integer的默认范围是 -128~127
最小值不能变
但最大值可以通过调整虚拟机参数-Djava.lang.Integer.IntegerCache.high来改变 - Boolean 缓存了 TRUE 和 FALSE
非可共享享元(Unsharable Flyweight)角色
是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色
负责创建和管理(缓存)享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
示例
棋子,作为抽象享元角色,分为白子和黑子,白子和黑子对象可以作为可共享对象,充当具体享元角色,棋子的坐标由于一直变化,所以是不可共享对象,作为参数传入
组合模式
又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
对于组合模式而言,在安全性和透明性上,==会更看重透明性==,毕竟组合模式的功能就是要让用户对叶子对象和组合对象的使用具有一致性。
透明式的组合模式
抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝(容器)对象,对客户端来说是透明的。
缺点
树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常)
示例

1 | public class CompositePattern |
安全式的组合模式
将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题。
缺点
由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
示例
1 | /* |
组合模式扩展
对树叶和树枝进行抽象,然后继承得到不同的树叶和树枝
行为型模式(11种)
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。
模板方法
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
抽象类中方法类型
- 抽象方法:在抽象类中申明,由具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法:在抽象类中已经实现,表示默认方式,但是子类也可以重写,表示自定义方法
示例
1 | package templateMethod; |
策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要角色
抽象策略(Strategy)类
定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略(Concrete Strategy)类
实现了抽象策略定义的接口,提供具体的算法实现。
环境(Context)类
持有一个策略类的引用,最终给客户端调用。
示例
策略工厂模式
当需要频繁切换策略时,可以使用策略工厂模式来缓存策略对象,减少生成对象的时间
命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便对命令对象进行储存、传递、调用、增加与管理(日志记录)。
示例
1 | package command; |
宏命令模式
也叫组合命令模式,将命令模式与组合模式联合使用。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行它时将递归调用它所包含的所有命令
1 | public class CompositeCommandPattern |
责任链模式
为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
职责链模式主要包含以下角色
抽象处理者(Handler)角色
定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler)角色
实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色
创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程
示例
规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;
1 | public class LeaveApprovalTest |
状态模式
有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。
状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式包含以下主要角色
环境(Context)角色
也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
抽象状态(State)角色
定义一个接口,用以封装环境对象中的特定状态所对应的行为。
具体状态(Concrete State)角色
实现抽象状态所对应的行为。
示例

1 | package state; |
享元状态模式
当状态较多时,结合享元模式来缓存状态对象
观察者模式
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
示例
1 | public class ObserverPattern |
中介者模式
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
和观察者模式区别
观察者模式分为被观察者和观察者,由被观察者通知观察者
中介者模式每个对象即使观察者又是被观察者
示例
1 | //抽象中介者 |
简化中介者模式
- 不定义中介者接口,把具体中介者对象实现成为单例。
- 同事对象不持有中介者,而是在发消息时直接获取中介者对象并调用。
迭代器模式
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式
优点
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 遍历任务交由迭代器完成,这简化了聚合类。
- 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
- 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口
示例
1 | public class IteratorPattern |
迭代器模式扩展
将迭代器模式和组合模式结合,实现对组合模式构成的树的遍历。迭代器存放在树枝组件中
访问者模式
有些集合对象中存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。
访问者(Visitor)模式就是将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
示例
1 | public class VisitorPattern |
访问者模式扩展
和迭代器模式联合
当对象结构中的聚合类没有提供迭代器时,可以用迭代器模式自定义一个。
和组合模式联合
访问者(Visitor)模式中的“元素对象”可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式
备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
主要角色
发起人(Originator)角色
记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
备忘录(Memento)角色
负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
管理者(Caretaker)角色
对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
1 | //备忘录 |
备忘录模式扩展
备忘录模式和原型模式联合
在备忘录模式中,备忘录就是用来保存发起人内部信息的类,如果发起人内部所有信息都要被记录,则可以直接使用原型模式的clone方法(浅拷贝),来复制一个相同的类,然后将该类作为备忘录保存在管理者类中。
解释器模式
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子
由于效率很低,不常用
示例
1 | /*文法规则 |
Java 常用解析器
Java 提供了以下强大的数学公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep (Java expression parser )等,它们可以解释一些复杂的文法,功能强大,使用简单。
示例
1 | //使用Jep |
UML类图
UML图分类
- 用例图
- 静态结构图:类图、对象图、包图、组件图、部署图
- 动态行为图:交互图(时序图与协作图)、状态图、活动图
UML类图简介
用于描述系统中类本身组成和类之间的各种静态关系
类本身组成
在UML类图中,类使用包含类名、属性(field) 和方法(method) 且带有分割线的矩形来表示,比如下图表示一个Employee类,它包含name,age和email这3个属性,以及modifyInfo()方法。
符号解释
1 | -表示private |
类之间的关系
- 依赖(下面几种其实都是特殊的依赖关系)
- 泛化
- 实现
- 关联
- 聚合
- 组合
依赖
介绍
对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系
表示方法
依赖关系用虚线箭头表示。
示例
动物依赖氧气和水。调用新陈代谢方法需要氧气类与水类的实例作为参数

泛化(继承)
介绍
继承表示是一个类(称为子类)继承另外的一个类(称为父类)的功能,并可以增加它自己的新功能的能力。
表示方法
继承使用空心三角形+实线表示。
示例
鸟类继承抽象类动物

实现
介绍
实现表示一个class类实现interface接口(可以是多个)的功能。
表示方法
使用空心三角形+虚线表示
示例
大雁和飞机需要飞行,就要实现飞()接口
关联
介绍
对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系
关联的性质
- 导航性
- 单向关联
- 双向关联
- 自关联
- 多重性
- “1”有且仅有一个
- “0…”0个或多个
- “0,1”0个或者1个
- “n…m”范围[n,m]中的一个
- “m…*”范围[m,+∞)中的一个
单向关联
表示方法
使用带箭头的直线表示
示例
每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现

双向关联
表示方法
使用不带箭头的直线表示
示例
在Customer类中维护一个Product[]数组,表示一个顾客购买了那些产品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买

自关联
是一个特殊的单向关联

聚合
介绍
表示一种弱的‘拥有’关系,即has-a的关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。 两个对象具有各自的生命周期。
表示方法
聚合关系用空心的菱形+实线箭头表示。
示例
每一只大雁都属于一个大雁群,一个大雁群可以有多只大雁。当大雁死去后大雁群并不会消失,两个对象生命周期不同。

组合
介绍
组合是一种强的‘拥有’关系,是一种contains-a的关系,体现了严格的部分和整体关系,部分和整体的生命周期一样。
表示方法
组合关系用实心的菱形+实线箭头表示,还可以使用连线两端的数字表示某一端有几个实例。
示例
鸟和翅膀就是组合关系,因为它们是部分和整体的关系,并且翅膀和鸟的生命周期是相同的。
