14-命令模式

命令模式

分析

开关与电灯、排气扇示意图

image-20221213203820194

分析

现实生活

  • 相同的开关可以通过不同的电线来控制不同的电器

  • 开关 \longleftrightarrow 请求发送者

  • 电灯 \longleftrightarrow请求的最终接收者和处理者

  • 开关和电灯之间并不存在直接耦合关系,它们通过电线连接在一起,使用不同的电线可以连接不同的请求接收者

image-20221213204319815

发送者与接受者彻底地解耦

动机
  • 将请求发送者和接收者完全解耦

  • 发送者与接收者之间没有直接引用关系

  • 发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求

命令模式的定义

image-20221219190624768

对象行为型模式

命令对象

也可以处理一系列请求操作

  • 别名为动作(Action)模式事务(Transaction)模式

  • “用不同的请求对客户进行参数化”

  • “对请求排队”

    • 多个命令存入队列中
  • “记录请求日志”

  • “支持可撤销操作”

命令模式结构

image-20221219191436230

命令模式包含以下4个角色:
  • Command(抽象命令类)

  • ConcreteCommand(具体命令类)

  • Invoker(调用者/发送者)

  • Receiver(接收者)

命令模式实现
  1. 命令模式的本质是对请求进行封装

  2. 一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开

  3. 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的

典型的抽象命令类代码
1
2
3
public abstract class Command {
public abstract void execute();
}
典型的调用者(请求发送者)类代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Invoker {
private Command command;
//构造注入
public Invoker(Command command) {
this.command = command;
}
//设值注入
public void setCommand(Command command) {
this.command = command;
}
//业务方法,用于调用命令类的execute()方法
public void call() {
command.execute();
}
}
典型的具体命令类代码
1
2
3
4
5
6
7
public class ConcreteCommand extends Command {
private Receiver receiver; //维持一个对请求接收者对象的引用

public void execute() {
receiver.action(); //调用请求接收者的业务处理方法action()
}
}

具体命令类可以与接受者类之间是聚合关系或是组合关系

典型的请求接收者类代码
1
2
3
4
5
public class Receiver {
public void action() {
//具体操作
}
}
实例类图:

image-20221219192943937

image-20221219192955525

(1) FunctionButton:功能键类,充当请求调用者(请求发送者)

(2) Command:抽象命令类

(3) ExitCommand:退出命令类,充当具体命令类

(4) HelpCommand:帮助命令类,充当具体命令类

(5) SystemExitClass:退出系统模拟实现类,充当请求接收者

(6) DisplayHelpClass:显示帮助文档模拟实现类,充当请求接收者

(7) Client:客户端测试类

  • 如果需要更换具体命令类,无须修改源代码,只需修改配置文件完全符合开闭原则

  • 每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,用不同的请求对客户进行参数化”,客户端只需要将具体命令对象作为参数注入请求发送者,无须直接操作请求的接收者

配置文件
1
2
3
4
<?xml version="1.0"?>
<config>
<className>designpatterns.command.ExitCommand</className>
</config>

实现命令队列

动机
  1. 当一个请求发送者发送一个请求时,有不止一个请求接收者产生响应这些请求接收者将逐个执行业务方法,完成对请求的处理

  2. 增加一个CommandQueue类,由该类负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者

  3. 批处理

CommandQueue类实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.*;
public class CommandQueue {
//定义一个ArrayList来存储命令队列
private ArrayList<Command> commands = new ArrayList<Command>();

public void addCommand(Command command) {
commands.add(command);
}

public void removeCommand(Command command) {
commands.remove(command);
}

//循环调用每一个命令对象的execute()方法
public void execute() {
for (Object command : commands) {
((Command)command).execute();
}
}
}
相应的发送者代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Invoker {
//维持一个CommandQueue对象的引用
private CommandQueue commandQueue;

//构造注入
public Invoker(CommandQueue commandQueue) {
this. commandQueue = commandQueue;
}

//设值注入
public void setCommandQueue (CommandQueue commandQueue) {
this.commandQueue = commandQueue;
}

//调用CommandQueue类的execute()方法
public void call() {
commandQueue.execute();
}
}

可以用顺序图来展现对象调用的顺序

记录请求日志

动机
  1. 将请求的历史记录保存下来,通常以日志文件(Log File)的形式永久存储在计算机中

    • 为系统提供一种恢复机制

    • 可以用于实现批处理

    • 防止因为断电或者系统重启等原因造成请求丢失,而且可以避免重新发送全部请求时造成某些命令的重复执行

实现
  • 将发送请求的命令对象通过序列化写到日志文件中

  • 命令类必须实现接口Serializable

image-20221219194349557

实现撤销操作

实例类图
  1. 可以通过对命令类进行修改使得系统支持==撤销(Undo)操作和恢复(Redo)==操作

image-20221219194520157

类图结构:

image-20221219194544170

  • 加法类:Adder(请求接收者)

  • 抽象命令类:AbstractCommand

  • 加法命令类:AddCommand(具体命令类)

  • 计算器界面类:CalculatorForm(请求发送者)

  • 客户端测试类:Client

不同的场景所实现Undo或Redo的方式不同

宏命令

动机
  1. 宏命令(Macro Command)又称为组合命令(Composite Command),它是组合模式和命令模式联用的产物

  2. 宏命令是一个具体命令类,它拥有一个集合,在该集合中包含了对其他命令对象的引用

  3. 当调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法一个宏命令的成员可以是简单命令,还可以继续是宏命令

  4. 执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理

类图结构

image-20221219195123146

模式优点

  • 降低系统的耦合度

  • 新的命令可以很容易地加入到系统中,符合开闭原则

  • 可以比较容易地设计一个命令队列或宏命令(组合命令)

  • 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案

模式缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类针对每一个对请求接收者的调用操作都需要设计一个具体命令类

模式适用环境

  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互

  • 系统需要在不同的时间指定请求、将请求排队和执行请求

  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作

  • 系统需要将一组操作组合在一起形成宏命令

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2024 Guijie Wang
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信