一、模式动机
命令模式(Command Pattern)是一种常用的行为型设计模式,它将请求发送者与请求接收者解耦,请求发送者通过命令对象来间接引用接收者,使得系统具有更好的灵活性,可以在不修改现有系统源代码的情况下将相同的发送者对应不同的接收者,也可以将多个命令对象组合成宏命令,还可以在命令类中提供用来撤销请求的方法。
命令模式又称为“动作(Action)模式”或者“事务(Transaction)模式”,是对象行为型模式之一。
- 现实生活
- 相同的开关可以通过不同的电线来控制不同的电器
- 开关 类比 请求发送者
- 电灯 类比 请求的最终接收者和处理者
- 开关和电灯之间并不存在直接耦合关系,它们通过电线连接在一起,使用不同的电线可以连接不同的请求接收者
- 软件开发
- 按钮 相当于 请求发送者
- 事件处理类 相当于 请求的最终接收者和处理者
- 发送者与接收者之间引入了新的命令对象(类似电线),将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法
- 相同的按钮可以对应不同的事件处理类
- 将请求发送者和接收者完全解耦
- 发送者与接收者之间没有直接引用关系
- 发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
二、模式定义
- 命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
- 命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式
- “用不同的请求对客户进行参数化”
- “对请求排队”
- “记录请求日志”
- “支持可撤销操作”
三、模式结构
四、案例实现
案例背景
电视机遥控器
案例结构
代码实现
电视机类
public class Television {
public void open(){
System.out.println("打开电视");
}
public void close(){
System.out.println("关闭");
}
public void change(){
System.out.println("换台");
}
}
抽象命令类
public interface Command {
void excute();
}
打开命令
public class OpenCommand implements Command{
private Television t;
public OpenCommand(){
t = new Television();
}
@Override
public void excute() {
t.open();
}
}
关闭命令
public class CloseCommand implements Command{
private Television t;
public CloseCommand() {
t = new Television();
}
@Override
public void excute() {
t.close();
}
}
换台命令
public class ChangeCommand implements Command{
private Television t;
public ChangeCommand() {
t = new Television();
}
@Override
public void excute() {
t.change();
}
}
电视机遥控器
public class Controller {
private Command openCommand,closeCommand,changeCommand;
public Controller(Command openCommand, Command closeCommand, Command changeCommand) {
this.openCommand = openCommand;
this.closeCommand = closeCommand;
this.changeCommand = changeCommand;
}
public void open(){
openCommand.excute();
}
public void close(){
closeCommand.excute();
}
public void change(){
changeCommand.excute();
}
}
客户类
public class Client {
public static void main(String[] args) {
Command openCommand,closeCommand,changeCommand;
openCommand = new OpenCommand();
closeCommand = new CloseCommand();
changeCommand = new ChangeCommand();
Controller controller = new Controller(openCommand,closeCommand,changeCommand);
controller.open();
controller.close();
controller.change();
}
}
案例结果
案例分析
调用者与抽象命令相关联,在程序运行时注入具体命令类对象,在Controller的业务方法中调用命令类的excute(),不同命令类的excute()不同,可以调用请求接受者的不同请求响应方法。
每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,用不同的请求对客户进行参数化”,客户端只需要将具体命令对象作为参数注入请求发送者,无须直接操作请求的接收者
五、模式分析
- 命令模式的本质是对请求进行封装
- 将请求发送者和接收者完全解耦
- 使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
- 发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
- 命令模式的本质是对请求进行封装
- 一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开
六、总结
模式优点
- 降低系统的耦合度
- 新的命令可以很容易地加入到系统中,符合开闭原则
- 可以比较容易地设计一个命令队列或宏命令(组合命令)
- 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案
模式缺点
- 使用命令模式可能会导致某些系统有过多的具体命令类(针对每一个对请求接收者的调用操作都需要设计一个具体命令类)
使用情形
- 需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
- 需要在不同的时间指定请求、将请求排队和执行请求
- 需要支持命令的撤销(Undo)操作和恢复(Redo)操作
- 需要将一组操作组合在一起形成宏命令