1. 概述

  • 行为型模式:
    关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责,共有11种模式。

2. 责任链模式(Chain Of Responsibility)

定义:
将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。

场景:

  • 打牌时,轮流出牌
  • 击鼓传花
  • 接力赛跑
  • 大学中,奖学金审批
  • 公司中,公文逐级审批

责任链可以通过链表,或者List来实现。

设计模式GOF23之第三回_子类

3. 命令模式(Command)

命令模式:

  • 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。也称之为:动作Action模式、事务Transaction模式。

设计模式GOF23之第三回_子类_02


设计模式GOF23之第三回_父类_03

4. 解释器模式(Interpreter)

  • 用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计。
  • 尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用Jruby,Groovy、java的js引擎来替代解释器的作用,弥补java语言的不足。
  • 数学表达式解析器
    如现成的工具包:MESP(Math Expression String Parser)、Expression4J等。
    MESP:https://sourceforge.net/projects/expression-tree/
    Expression4J:https://sourceforge.net/projects/expression4j/

5. 迭代器模式(Iterator)

场景:

  • 提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
  • 聚合对象:存储数据
  • 迭代器:遍历数据

设计模式GOF23之第三回_子类_04


实现的类图

设计模式GOF23之第三回_中介者_05


迭代器一般通过内部类来实现,这样可以方便地访问外部类中的属性和方法。

6. 中介者模式(Mediator)

  • 核心:
    如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂,这些对象称为“同事对象”
    我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星形结构。
  • 设计模式GOF23之第三回_中介者_06


  • 设计模式GOF23之第三回_中介者_07


  • 设计模式GOF23之第三回_父类_08

  • 中介者模式的本质:
  • 解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系。

7. 备忘录模式(Memento)

场景:

  • 录入大批人员资料。正在录入当前人资料时,发现上一个人录错了,此时需要恢复上一个人的资料,再进行修改。
  • Word文档编辑时,忽然电脑死机或断电,再打开时,可以看到word提示你恢复到以前的文档。
  • 管理系统中,公文撤回功能。公文发送出去后,想撤回来。

设计模式GOF23之第三回_父类_09


备忘点较多时:

  • 将备忘录压栈保存
public class CareTaker {
private Memento memento;
private Stack<Memento> stack = new Stack<Memento>();
}
  • 将多个备忘录对象,序列化和持久化。

开发中常见的应用场景:

  • 棋类游戏中的,悔棋。
  • 普通软件中的,撤销操作。
  • 数据库软件中的,事务管理中的,回滚操作。
  • Photoshop软件中的,历史记录。

8. 观察者模式(Observer)

场景:

设计模式GOF23之第三回_子类_10


主要用于消息的广播,消息订阅与发布。

通知观察者的方式:


  • 每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收。

  • 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。

设计模式GOF23之第三回_子类_11


注意:

Subject要将某个或者某些状态同步/广播给Observers。

public class Subject {

protected List<Observer> list = new ArrayList<Observer>();

public void registerObserver(Observer obs) {
list.add(obs);
}

public void removeObserver(Observer obs) {
list.remove(obs);
}

// 通知所有的观察者更新状态
public void notifyAllObservers() {
for (Observer obs : list) {
//这里的 this 把Subject传给Observer。Observer持有Subject后调用Subject.states来更新Observer自己的states
obs.update(this);
}
}

}

自定义实现的类图:

设计模式GOF23之第三回_中介者_12


JDK提供了 java.util.Observablejava.util.Observer 来帮助实现观察者模式。

9. 状态模式(State)

场景:
不同的状态对应不同的行为。比如交通灯,红灯停,绿灯行,黄灯减速。
又比如,
酒店系统中,房间的状态变化:

  • 已预订
  • 已入住
  • 空闲
  • 设计模式GOF23之第三回_子类_13

  • 不使用状态模式,使用分支判断,可扩展性很差——一有变化就要改原来的代码。
if(state=="空闲"){
if(预订房间){
预定操作;
state="已预订";
}else if(住进房间){
入住操作;
state="已入住";
}
}else if(“已预订"){
if(住进房间){
入住操作;
state="已入住";
}else if(取消预订){
取消操作;
state="空闲";
}
}

设计模式GOF23之第三回_java_14


由RoomContext来持有房间的当前状态。

10. 策略模式(Strategy)

问题的提出:
某个市场人员接到单后的报价策略(CRM系统中常见问题)。报价策略很复杂,可以简单作如下分类:

  • 普通客户小批量报价
  • 普通客户大批量报价
  • 老客户小批量报价
  • 老客户大批量报价

具体选用哪个报价策略,这需要根据实际情况来确定。

public double getPrice(String type, double price){

if(type.equals("普通客户小批量")){
System.out.println("不打折,原价");
return price;
}else if(type.equals("普通客户大批量")){
System.out.println("打九折");
return price*0.9;
}else if(type.equals("老客户小批量")){
System.out.println("打八五折");
return price*0.85;
}else if(type.equals("老客户大批量")){
System.out.println("打八折");
return price*0.8;
}
return price;
}

假如,类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难于维护。

策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法。并且由客户端决定调用哪个算法。

本质:
通过接口中的方法来实现多态。

设计模式GOF23之第三回_子类_15

11. 模板方法模式(Template Method)

场景:
有固定的流程,但不知道具体的实现。比如下图中的请客。

设计模式GOF23之第三回_中介者_16

其他例子还有

  • 客户到银行办理业务:
  • 取号排队
  • 办理具体现金/转账/企业/个人/理财业务
  • 给银行工作人员评分

模板方法模式是编程中经常用得到模式。它定义了一个操作中的算法​​骨架​​​,将某些步骤​​延迟到子类中实现​​。这样,新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤。

核心:
处理某个流程的代码已经都具备(流程步骤都知道),但是其中某个节点的代码暂时不能确定。因此,我们采用工厂方法模式,将这个节点的代码实现转移给子类完成。即:处理步骤父类中定义好,具体实现延迟到子类中实现。

注意:
这里的延迟到子类中实现,本质上就是利用抽象类的抽象方法的多态特性。

模版方法模式也称——方法回调,钩子方法。
好莱坞原则:“​​​Don’t call me , we’ll call you back​​​”
在好莱坞,当艺人(子类)把简历递交给好莱坞的娱乐公司(父类)时,所能做的就是等待,整个过程由娱乐公司控制,演员只能被动地服从安排,在需要的时候再由公司安排具体环节的演出。

在软件开发中,我们可以将call翻译为调用。子类不能调用父类,而通过父类调用子类。这些调用步骤已经在父类中写好了,完全由父类控制整个过程。

什么时候用到模板方法模式:
实现一个算法时,整体步骤很固定。但是,某些部分易变。易变部分可以抽象成出来,供子类实现。

12. 访问者模式(Visitor)

  • 模式动机:
    对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式也有所不同。
  • 定义:
    表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变个元素的类的前提下定义作用于这些元素的新操作。
  • 开发中的场景(应用范围非常窄,了解即可):
    XML文档解析器设计
    编译器的设计
    复杂集合对象的处理