设计模式(十五) 命令模式

Command Pattern

1. 什么是命令模式

命令模式(Command Pattern):将一个请求封装为一个对象,从而使用不同的参数调用不同的请求
命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。

在命令模式中,发送者与接收者之间引入了新的命令对象,将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。
命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的

命令模式的核心在于引入了命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法。
命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。

2. 命令模式类角色解析

命令模式结构图:
命令模式

  • Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
  • ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。
  • Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。**一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。**在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
  • Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

3. 扩展用法

3.1 多个抽象方法

抽象命令类Command中,可以添加多个抽象方法,进行不同的操作。

3.2 宏命令:

和组合模式连用,构成宏命令,又称为组合命令
宏命令是一个具体命令类,它拥有一个集合属性,在该集合中包含了对其他命令对象的引用。
当调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法,一个宏命令的成员可以是简单命令,还可以继续是宏命令。
宏命令

3.3 使用命令队列

一个请求发送者发送一个请求时,会有多个请求接收者(Command)响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。

将多个请求接受者(Command)封装在CommandQueue中。

代码如下:

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 (Command command : commands) {  
            command.execute();  
        }  
    }  
}  

相应的Invoker中代码:

class Invoker {  
    private CommandQueue commandQueue; //维持一个CommandQueue对象的引用  
      
    //构造注入  
    public Invoker(CommandQueue commandQueue) {  
        this. commandQueue = commandQueue;  
    }  
      
    //设值注入  
    public void setCommandQueue(CommandQueue commandQueue) {  
        this.commandQueue = commandQueue;  
    }  
      
    //调用CommandQueue类的execute()方法  
    public void call() {  
        commandQueue.execute();  
    }  
}  

4. 实操感想

在实践中,应该正确区分出谁是Invoker,谁是Receiver,再用Command将他们联系起来。

Receiver如果具有共同的方法,可以放在抽象类Command中,避免了在每个ConcreteCommand中创建接收者。

这种模式分离了发出命令者和响应命令者,而且面对抽象Command编程,将具体Command设置给发出命令者即可,可以很方便的替换响应对象,也可以很方便的添加一个响应对象。具体可以参考下面这个示例,很容易理解。

5. 代码示例

一个界面中,每个Button的功能不尽相同,有的打开帮助文档,有的使该界面最小化等等。

/*
抽象命令类,其子类持有请求接收者,在execute方法中调用该接受者来处理请求。

命令模式分离了请求的发送者和接受者,请求的发送者调用命令类execute()方法,命令类在此方法中转接到真正的接受者处理方法action中.这就是命令模式.

所有的请求者都封装在Invoker中,并且面对Command编程,使之解耦.需要使用不同的具体功能时,替换Command即可
*/
abstract class Command {
    public abstract void execute();
}

//具体命令类,打开帮助文档。持有Receiver,真正的请求处理类。
public class CommandHelper extends Command {
    private HandlerHelper handler;

    public CommandHelper() {
        handler = new HandlerHelper();
    }

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

public class HandlerHelper {
    public void action() {
        System.out.println("打开帮助文档");
    }
}

//具体命令类,最小化。
public class CommandMinimize extends Command {
    private HandlerMinimize handler;

    public CommandMinimize() {
        handler = new HandlerMinimize();
    }

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

public class HandlerMinimize {
    public void action() {
        System.out.println("最小化");
    }
}

/*
Invoker类,封装真正的请求,持有Command类,调用Command类的execute()方法。
*/
class Button {
    private String name;
    private Command command;

    public Button(String name) {
        this.name = name;
    }
    
    public void setCommand(Command command) {
        this.command = command;
    }

    public void onClick() {
        System.out.println("点击按钮" + name);
        command.execute();
    }
}

//界面对象,持有很多Button
class SettingWindow {
    private String title;
    private List<Button> buttons = new ArrayList<>();

    public SettingWindow(String title) {
        this.title = title;
    }

    public void addButton(Button button) {
        buttons.add(button);
    }

    public void removeButton(Button button) {
        buttons.remove(button);
    }


    public void testDisplay() {
        for (Button button : buttons) {
            button.onClick();
        }
    }
}

//测试类
class ZZMainClass {
    public static void main(String[] args) {
        SettingWindow settingWindow = new SettingWindow("一个界面");

        //创建按钮
        Button buttonOne = new Button("Button One");
        buttonOne.setCommand(new CommandHelper());//设置该Button响应命令的对象

        Button buttonTwo = new Button("Button Two");
        buttonTwo.setCommand(new CommandMinimize());//设置该Button响应命令的对象

        buttonOne.onClick();//点击,触发命令
        buttonTwo.onClick();//点击,触发命令

		//此处如果需要额外的响应事件,比如最大化,则继承Command类实现一个最大化命令响应对象即可。很方便。更改响应命令对象更方便,直接调用button.setCommand(新的Command)即可。

        //放入一个Window中
        settingWindow.addButton(buttonOne);
        settingWindow.addButton(buttonTwo);

        //settingWindow.testDisplay();//测试一下
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值