掌握设计模式--命令模式

命令模式(Command Pattern)

命令模式(Command Pattern)是一种行为型设计模式,它将请求(命令)封装成对象,从而使您能够参数化客户端(调用者)使用不同的请求、队列请求或者日志请求,甚至支持可撤销的操作。

核心思想将请求封装成对象,从而使得调用者与接收者解耦,同时支持请求的存储、撤销、重做和排队执行。

主要组成部分

  1. Command(命令接口) :通常是一个接口,定义一个执行命令的 execute() 方法。
  2. ConcreteCommand(具体命令):实现命令接口,并定义执行该命令的具体操作,通常将请求的接收者(Receiver)和操作绑定在一起。
  3. Client(客户端):创建一个具体命令对象并设置其接收者(Receiver)。
  4. Invoker(调用者) :请求命令的执行。通常在用户操作时,会调用 execute() 方法。
  5. Receiver(接收者):知道如何实施与执行一个请求相关的操作,实际的业务逻辑通常由此类执行。

案例实现

一个 图形编辑器,用户可以执行对图形的操作(如绘制、擦除等),并能够撤销这些操作。

案例类图

命令调用者类依赖于命令接口,命令接口下的具体命令类操作实际的业务逻辑

Command接口

java 复制代码
public interface Command {
    void execute();
    void undo();
}

绘制图形命令

java 复制代码
// ConcreteCommand - 绘制图形命令
public class DrawShapeCommand implements Command {
    private Shape shape;

    public DrawShapeCommand(Shape shape) {
        this.shape = shape;
    }

    @Override
    public void execute() {
        shape.draw();
    }

    @Override
    public void undo() {
        shape.erase();
    }
}

// ConcreteCommand - 删除图形命令
class EraseShapeCommand implements Command {
    private Shape shape;

    public EraseShapeCommand(Shape shape) {
        this.shape = shape;
    }

    @Override
    public void execute() {
        shape.erase();
    }

    @Override
    public void undo() {
        shape.draw();
    }
}

接受者--抽象图形类

java 复制代码
public abstract class Shape {
    protected String name;

    public abstract void draw();
    public abstract void erase();
}

接受者--具体图形类

java 复制代码
public class Circle extends Shape {
    public Circle() {
        this.name = "Circle";
    }

    @Override
    public void draw() {
        System.out.println("绘制图形 " + name);
    }

    @Override
    public void erase() {
        System.out.println("擦除图形 " + name);
    }
}

class Rectangle extends Shape {
    public Rectangle() {
        this.name = "Rectangle";
    }

    @Override
    public void draw() {
        System.out.println("绘制图形 " + name);
    }

    @Override
    public void erase() {
        System.out.println("擦除图形 " + name);
    }
}

命令调用者

java 复制代码
public class CommandInvoker {
    private Stack<Command> commandHistory = new Stack<>();

    public void executeCommand(Command command) {
        command.execute();
        commandHistory.push(command);
    }

    public void undo() {
        if (!commandHistory.isEmpty()) {
            Command lastCommand = commandHistory.pop();
            lastCommand.undo();
        } else {
            System.out.println("No commands to undo.");
        }
    }
}

测试代码

java 复制代码
public class CommandDemo {
    public static void main(String[] args) {
        // 创建图形
        Shape circle = new Circle();
        Shape rectangle = new Rectangle();

        // 创建命令
        Command drawCircleCommand = new DrawShapeCommand(circle);
        Command drawRectangleCommand = new DrawShapeCommand(rectangle);
        Command eraseCircleCommand = new EraseShapeCommand(circle);
        Command eraseRectangleCommand = new EraseShapeCommand(rectangle);

        // 创建命令调用者
        CommandInvoker invoker = new CommandInvoker();

        // 执行命令
        invoker.executeCommand(drawCircleCommand);  // 绘制圆形
        invoker.executeCommand(drawRectangleCommand);  // 绘制矩形
        invoker.executeCommand(eraseCircleCommand);  // 删除圆形
        invoker.executeCommand(eraseRectangleCommand);  // 删除矩形

        // 撤销操作
        invoker.undo();  // 撤销删除矩形
        invoker.undo();  // 撤销删除圆形
        invoker.undo();  // 撤销绘制矩形
        invoker.undo();  // 撤销绘制圆形
        invoker.undo();
    }
}

测试结果

绘制图形 Circle

绘制图形 Rectangle

擦除图形 Circle

擦除图形 Rectangle

绘制图形 Rectangle

绘制图形 Circle

擦除图形 Rectangle

擦除图形 Circle

No commands to undo.

优缺点和适用场景

优点:

  1. 解耦请求者和接收者:请求者(客户端)不需要知道接收者的具体实现,只需要知道命令接口。
  2. 支持撤销操作:可以将命令对象设计为支持撤销的操作,使得某些操作能够撤回。
  3. 可以将命令参数化:命令可以作为参数传递,或被存储起来,支持批量操作。
  4. 扩展性好:增加新的命令时,不需要改变现有代码,只需要新增具体命令类。

缺点:

  1. 增加类的数量:每个具体命令类都需要创建一个类,可能导致类的数量增多。
  2. 实现复杂度:如果系统中的命令非常多,可能导致命令类实现过于复杂。

命令模式在 GUI 程序、事务管理、队列任务等场景中非常常见。

适用场景

  • 需要解耦请求者和接收者。
  • 需要撤销、重做操作。
  • 需要存储请求、支持队列、日志功能。
  • 需要动态选择操作或扩展操作。
  • 需要将多个操作封装为一个命令。
  • 需要管理跨平台或多设备的操作。

总结

命令模式的核心关注点是将请求封装成对象,从而使得请求的发送者(调用者)和接收者(执行者)解耦。命令模式通过把请求封装成命令对象,使得你可以在不改变请求者的情况下改变请求的执行方式、顺序或者操作对象。

  • 行为封装:命令模式将请求、操作或事务封装为命令对象,这些对象可以被请求者调用。请求者不关心具体操作的执行方式,只需要调用命令对象的执行方法即可。

  • 请求者和执行者解耦:通过引入命令对象,调用者和被调用者的关系被解耦,调用者不需要知道如何执行操作,也不需要知道具体的操作是什么,只需要发出命令请求。

需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。


超实用的SpringAOP实战之日志记录

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

软考中级--软件设计师毫无保留的备考分享

相关推荐
杨充12 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充13 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充13 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充13 小时前
2.面向对象的特性
设计模式
杨充13 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充13 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充13 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯16 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式