设计模式扫盲系列(四):行为型模式集

【引言】此篇主要讨论11种常见的行为型模式:责任链模式(Chain of Responsibility Pattern)、命令模式(Command Pattern)、解释器模式(Interpreter Pattern)、迭代器模式(Iterator Pattern)、中介者模式(Mediator Pattern)、备忘录模式(Memento Pattern)、观察者模式(Observer Pattern)、状态模式(State Pattern)、策略模式(Strategy Pattern)、模板模式(Template Pattern)、访问者模式(Visitor Pattern)。点击查看完整示例代码


责任链模式(Chain of Responsibility Pattern)


定义

  为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
  实际形成的结构就如同一条链表,从头结点调用,若对应节点无法处理,则流程转交给下一个节点处理,直至链尾。

角色

  责任链模式主要包含以下角色:

  • 抽象处理者(Handler):一般来说定义为一个抽象类(对外实际上也可以称之为接口),其中包含了抽象处理方法(由子类实现)和一个后继连接(也就是成链的连接点)。
  • 具体处理者(Concrete Handler):实际的处理类,一般需要实现抽象处理者定义的处理方法,先判断自己是否可以处理本次请求,如果可以则处理完结束,否则将该请求转给它的后继者。
  • 客户类(Client):一般也会抽象成一个方法(当然演示时我们一般直接写个main方法了),用于创建处理链,并向链头下发处理请求,它不需要知道具体是哪个节点完成的这次处理。

类图

  通过类图我们很清楚的就可以和角色一一对应了,这里就不画蛇添足了;实际上本模式我们特别需要关心的是successor这条线,这个可以理解是一个自调用的过程,因为所有的处理节点对外来说都是Handler,只不过大家责任不一。

实践

抽象处理者

  一个后继节点的引用,一个抽象的处理方法,很清晰明了的定义了这个抽象处理者,它不干活,它只是把需要干的活儿抽象出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.linc.dp.ChainOfResponsibility;

import lombok.Getter;
import lombok.Setter;

/**
* 抽象处理者
*
* @author Lin.C
* @date 2019/6/14 7:22
*/
public abstract class Handler {

@Getter
@Setter
private Handler next;

/**
* 处理请求的抽象方法
*
* @param request
*/
public abstract void handleRequest(String request);
}

具体处理者

  这个具体处理者可能有N个,这里只列举其中一个,实际上的实现逻辑是类似的,只不过对request的处理判断条件略有区别,一旦某个处理者处理不了而且它又没有后继节点了,那么就意味着这个链以无法处理结束了此次行程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.linc.dp.ChainOfResponsibility;

/**
* 具体的Handler
*
* @author Lin.C
* @date 2019/6/14 7:25
*/
public class ConcreteHandler1 extends Handler {

@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("I am ConcreteHandler1 dealing request.");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("No handler can deal this request.");
}
}
}
}

责任链工具类

  这个类不一定需要,这里只是为了把链本身封装起来而做的,它的内部实际上就是把链的各个节点间的关系组装好,对外就完全不用管我内部是如何调度的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.linc.dp.ChainOfResponsibility;

/**
* 工具类
*
* @author Lin.C
* @date 2019/6/14 7:29
*/
public class HandlerUtil {

/**
* 对外提供的处理方法
*
* @param request
*/
public static void doHandle(String request) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
handler1.handleRequest(request);
}
}

客户类

  客户自然就是考虑调用越简单越好,我管你内部谁来处理呢,只要给我把请求处理掉就OK了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.linc.client;

import com.linc.dp.ChainOfResponsibility.HandlerUtil;

/**
* 责任链客户端
*
* @author Lin.C
* @date 2019/6/14 7:31
*/
public class ChainOfResponsibilityClient {

public static void main(String[] args) {
HandlerUtil.doHandle("A");
HandlerUtil.doHandle("B");
HandlerUtil.doHandle("C");
}
}

// Output
I am ConcreteHandler1 dealing request.
I am ConcreteHandler2 dealing request.
No handler can deal this request.


命令模式(Command Pattern)


定义

  命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。略微有些抽象,所以还是要看看后面的实例。

角色

  命令模式主要包含以下角色:

  • 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  • 具体命令角色(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

类图

实现

命令接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.linc.dp.Command;

/**
* 命令接口
*
* @author Lin.C
* @date 2019/7/4 8:56
*/
public interface Command {

/**
* 执行命令
*/
void execute();
}

具体命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.linc.dp.Command;

/**
* 命令实现类
*
* @author Lin.C
* @date 2019/7/4 8:56
*/
public class ConcreteCommand implements Command {

private Receiver receiver;

public ConcreteCommand() {
receiver = new Receiver();
}

@Override
public void execute() {
receiver.action();
}
}

请求者

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
package com.linc.dp.Command;

import lombok.Getter;
import lombok.Setter;

/**
* 发送请求者
*
* @author Lin.C
* @date 2019/7/4 8:56
*/
public class Invoker {

@Getter
@Setter
private Command command;

public Invoker(Command command) {
this.command = command;
}

public void call() {
System.out.println("I am the invoker called...");
command.execute();
}
}

执行者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.linc.dp.Command;

/**
* 命令执行类
*
* @author Lin.C
* @date 2019/7/4 8:56
*/
public class Receiver {

public void action() {
System.out.println("I am the receiver doing my action...");
}
}

客户类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.linc.client;

import com.linc.dp.Command.ConcreteCommand;
import com.linc.dp.Command.Invoker;

/**
* 命令模式客户端
*
* @author Lin.C
* @date 2019/7/4 8:58
*/
public class CommandClient {

public static void main(String[] args) {
Invoker ir = new Invoker(new ConcreteCommand());
ir.call();
}
}

// Console output
I am the invoker called...
I am the receiver doing my action...

解释器模式(Interpreter Pattern)


定义

  解释器模式(Interpreter Pattern):给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

角色

  解释器模式包含以下主要角色:

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 上下文(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

类图

实践


迭代器模式(Iterator Pattern)



中介者模式(Mediator Pattern)



备忘录模式(Memento Pattern)



观察者模式(Observer Pattern)



状态模式(State Pattern)



策略模式(Strategy Pattern)



模板模式(Template Pattern)



访问者模式(Visitor Pattern)


------2019 Lin.C ------