设计模式 命令模式(Command Pattern)

命令模式简绍

命令模式(Command Pattern)是一种行为设计模式,它把请求封装成对象,从而让你可以用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

组成部分:

  • 命令接口(Command Interface):定义执行操作的接口。
  • 具体命令类(Concrete Command Class):实现了命令接口,并拥有接收者对象的引用。
  • 接收者(Receiver):知道如何实施与执行一个请求相关的操作,任何类都可以充当接收者。
  • 调用者(Invoker):请求一个命令对象执行一个请求。

命令模式的优缺点

优点
  • 降低耦合度:
    • 请求发送者不需要知道请求的实际接收者是谁,也不需要知道请求的执行细节。
    • 接收者和请求发送者之间解耦,使得两者可以独立变化。
  • 易于实现撤销和重做功能:
    • 由于每个命令都是一个对象,因此可以保存以前的状态,并在需要时恢复。
    • 支持事务回滚,因为可以存储多个命令,并按顺序或逆序执行。
  • 易于支持事务处理:
    • 可以将多个命令组合成一个宏命令(复合命令),从而实现整体的事务处理(要么全部成功,要么全部失败)。
  • 易于支持队列和日志:
    • 命令对象可以被放入队列中,按照顺序执行。
    • 命令对象也可以被记录下来,用于审计或日志记录。
  • 增加新的命令非常容易:
    • 添加新命令只需要实现命令接口,而不需要修改已有的代码。
    • 这符合开闭原则(Open-Closed Principle),即对扩展开放,对修改关闭。
  • 增强安全性:
    • 可以通过命令对象来验证权限,从而在执行之前检查是否有权限执行该命令。
缺点
  • 可能导致系统复杂度增加:
    • 如果系统中存在大量的命令类,可能会导致类的数量增加,进而使系统变得复杂。
    • 每个命令都需要一个具体的类来实现,这可能会导致类爆炸。
  • 内存消耗增加:
    • 如果命令对象被频繁创建,特别是在需要撤销或重做功能时,可能会导致内存消耗增加。
    • 特别是在没有及时清理不再需要的命令对象时,内存占用会更高。
  • 性能问题:
    • 命令模式增加了额外的抽象层,可能会导致一些性能上的损耗,尤其是在需要频繁执行命令的情况下。
    • 可能不适合简单场景:

对于非常简单的应用场景,使用命令模式可能会显得过于复杂,没有必要引入这种模式。

UML 图

实现代码

命令接口 (CommandInterface)

这个接口声明了所有命令对象都应该遵循的方法签名。

java 复制代码
public interface CommandInterface {
    void execute();

}
具体命令类 (ConcreteCommandClass)

这个类负责调用接收者的 openMobile() 方法,并添加了一些额外的操作(如"掏出口袋")。

java 复制代码
public class ConcreteCommandClass implements CommandInterface{
    private Receiver receiver;

    public ConcreteCommandClass(Receiver receiver){
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        System.out.println("掏出口袋");
        receiver.openMobile();
    }
}
具体命令类 (ConcreteCommandClass1)

这个类负责调用接收者的 closeMoblie() 方法,并添加了一些额外的操作(如"放回口袋")。

复制代码
public class ConcreteCommandClass1 implements CommandInterface{

    private Receiver receiver;

    public ConcreteCommandClass1(Receiver receiver){
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.closeMoblie();
        System.out.println("放回口袋");
    }
}
接收者

这个类包含了实际要执行的操作。

java 复制代码
public class Receiver {

    public void openMobile(){
        System.out.println("打开手机");
    }

    public void closeMoblie(){
        System.out.println("关闭手机");
    }

}
主调用 (Main)

在这里创建了具体的命令对象,并调用了它们的 execute() 方法。

java 复制代码
public class Main {
    public static void main(String[] args) {
        ConcreteCommandClass concreteCommandClass = new ConcreteCommandClass(new Receiver());
        concreteCommandClass.execute();
        ConcreteCommandClass1 concreteCommandClass1 = new ConcreteCommandClass1(new Receiver());
        concreteCommandClass1.execute();
    }
}

这个示例展示了命令模式的基本应用,通过将请求封装成对象,使得发送请求的对象和执行请求的对象之间松耦合。

命令模式应用场景

  • 用户界面设计中的按钮和菜单项

    在图形用户界面(GUI)应用程序中,命令模式可以用来处理用户交互事件。例如,当用户点击一个按钮或选择一个菜单项时,可以创建一个命令对象来表示这个请求,然后在适当的时候执行该命令。

  • 宏命令和批处理

    命令模式非常适合于构建宏命令或批处理命令。可以将多个简单的命令组合成一个复合命令,这样就可以一次执行一系列操作。例如,在文本编辑器中,可以定义一个宏来执行一系列编辑命令。

  • 支持撤销功能(Undo/Redo)

    命令模式可以很容易地支持撤销和重做功能。每个命令对象都可以记录其执行前后的状态,这样在需要撤销操作时可以恢复到之前的状态。这对于许多应用程序来说都是非常有用的特性,比如文本编辑器、绘图工具等。

  • 解耦发送者与接收者

    命令模式使得发送者和接收者之间的耦合度降低。发送者只需要知道如何发送命令,而不需要关心命令的具体实现细节。这使得系统更加灵活,可以在运行时动态改变命令的执行方式。

  • 支持事务处理

    在某些情况下,命令模式可以用来模拟事务处理。一组命令可以作为一个整体来执行,如果其中一个命令失败,则整个事务回滚。这对于一些需要保证数据一致性的场景非常有用。

  • 异步命令执行

    命令模式可以用来支持异步命令执行。命令对象可以在创建时立即被处理,也可以存储起来稍后由调度器统一执行。这对于需要异步处理的任务队列非常有用。

  • 多线程环境下的任务调度

    在多线程环境中,命令模式可以用来封装任务,并将其放入线程池中执行。这样可以方便地管理任务的执行顺序和优先级。

实际案例
  • 编辑器中的撤销功能:用户在编辑器中进行了一系列操作,每个操作都被封装成一个命令对象。这些命令对象可以存储在一个栈中,用户可以选择撤销上一步操作,实际上是从栈中取出最后一个命令对象并执行它的撤销方法。
  • 智能家居控制系统:用户可以通过语音助手控制家中的各种设备(如灯光、空调等)。语音助手接收到指令后,可以创建一个命令对象来表示这个请求,并通过网络发送给相应的设备进行处理。
相关推荐
杨充18 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充18 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充18 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充18 小时前
2.面向对象的特性
设计模式
杨充18 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充18 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充18 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯21 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式