当前位置: 首页 > article >正文

Java设计模式之命令模式

概念和作用

命令模式将请求封装为对象,使请求的发送者和接收者解耦。通过参数化请求、支持队列、日志、撤销等功能,增强系统的灵活性和扩展性。

场景

1.需要解耦请求发送者和接收者。

2.需要支持命令的排队、撤销/重做、日志记录。

3.需要动态指定请求或组合多个请求。

示例

// 1.命令接口
interface Command {
    void execute();
    void undo(); // 可选撤销功能
}
// 2.接收者(真正执行操作的对象)
class PhoneInventory {
    private int stock = 10;
    
    public void buyPhones(int quantity) {
        stock += quantity;
        System.out.println("采购成功,当前库存:" + stock);
    }
    
    public void sellPhones(int quantity) {
        if(stock >= quantity) {
            stock -= quantity;
            System.out.println("销售成功,当前库存:" + stock);
        } else {
            System.out.println("库存不足,销售失败");
        }
    }
}
// 3.具体命令:购买
class BuyCommand implements Command {
    private PhoneInventory inventory;
    private int quantity;

    public BuyCommand(PhoneInventory inventory, int quantity) {
        this.inventory = inventory;
        this.quantity = quantity;
    }

    @Override
    public void execute() {
        inventory.buyPhones(quantity);
    }

    @Override
    public void undo() {
        inventory.sellPhones(quantity);
    }
}
// 3.具体命令:销售
class SellCommand implements Command {
    private PhoneInventory inventory;
    private int quantity;

    public SellCommand(PhoneInventory inventory, int quantity) {
        this.inventory = inventory;
        this.quantity = quantity;
    }

    @Override
    public void execute() {
        inventory.sellPhones(quantity);
    }

    @Override
    public void undo() {
        inventory.buyPhones(quantity);
    }
}
// 4.调用者(触发命令的对象)
class OrderProcessor {
    private List<Command> commands = new ArrayList<>();

    // 添加命令
    public void addCommand(Command command) {
        commands.add(command);
    }

    // 执行命令
    public void processOrders() {
        for(Command cmd : commands) {
            cmd.execute();
        }
        commands.clear();
    }
}
// 5.客户端使用
public class Main {
    public static void main(String[] args) {
        // 接收者
        PhoneInventory inventory = new PhoneInventory();
        // 调用者
        OrderProcessor processor = new OrderProcessor();
        
        // 创建命令对象
        processor.addCommand(new BuyCommand(inventory, 5));
        processor.addCommand(new SellCommand(inventory, 3));
        
        // 统一执行命令
        processor.processOrders();
    }
}

优点和缺点

优点

1.解耦:调用者与具体操作解耦,调用者不需要知道具体实现。

2.可扩展:容易添加新命令,符合开闭原则。

3.组合命令:支持宏命令(组合多个命令)。

4.支持撤销:通过添加undo方法实现操作回滚。

缺点

1.类膨胀:每个命令都需要一个具体类。

2.复杂度增加:需要多层级对象协作。

不用命令模式的实现

// 直接调用库存方法
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        
        // 直接调用具体方法
        inventory.buyPhones(5);
        inventory.sellPhones(3);
    }
}

缺陷

1.紧耦合:调用者直接依赖具体实现。

// 不使用命令模式时
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        // 直接调用具体方法
        inventory.buyPhones(5);  // 直接依赖 buyPhones 方法
        inventory.sellPhones(3); // 直接依赖 sellPhones 方法
    }
}

问题说明:

调用者(客户端 main 方法)直接调用 PhoneInventory 的具体方法 buyPhones 和 sellPhones。如果 PhoneInventory 的方法名或参数改变(如 buyPhones 改名为 purchasePhones),所有调用它的代码都需要修改。

2.难以扩展:新增功能需要修改现有代码。

假设需要新增一个 退货功能 returnPhones(int quantity),此时:

// 非命令模式需要修改调用方
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        inventory.buyPhones(5);
        inventory.sellPhones(3);
        // 新增退货功能
        inventory.returnPhones(2); // 必须修改客户端代码
    }
}

问题说明:

调用者必须知道新方法 returnPhones 的存在并直接调用它。

违反开闭原则:扩展功能需要修改现有客户端代码。

对比命令模式:

命令模式只需新增 ReturnCommand 类,客户端通过添加命令对象即可扩展功能,无需修改现有调用逻辑。

3.无法支持撤销:需要额外维护操作历史。

在非命令模式中,若用户想要撤销一次销售操作:

public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        inventory.buyPhones(5);    // 初始库存 15
        inventory.sellPhones(3);    // 库存 12
        // 撤销销售操作(需要手动反向操作)
        inventory.buyPhones(3);     // 必须自行记录操作历史并反向调用
    }
}

问题说明:

调用者需自行记录每次操作的类型和参数(如销售了 3 台手机),并手动调用反向操作(如再采购 3 台)。

若操作历史复杂(如涉及多个对象),维护成本极高。

对比命令模式:

命令模式通过 Command 接口的 undo() 方法,自动记录操作历史并实现撤销。

4.缺乏灵活性:无法实现命令队列、延迟执行等高级功能。

假设需要将多个操作批量执行或延迟到特定时间执行:

public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        // 直接调用方法,无法延迟执行或批量管理
        inventory.buyPhones(5);
        inventory.sellPhones(3);
        // 若想实现延迟执行,需自行设计线程或定时任务
    }
}

问题说明:

调用者直接触发方法调用,所有操作立即执行。

若想实现命令队列、延迟执行或事务管理(如批量操作全部成功或全部失败),需额外开发复杂的逻辑。

对比命令模式:

命令模式通过 OrderProcessor 类管理命令队列,可轻松实现批量执行、延迟执行或事务回滚:

// 命令模式支持延迟执行
processor.addCommand(new BuyCommand(inventory, 5));
processor.addCommand(new SellCommand(inventory, 3));
// 在需要时统一执行
processor.processOrders();

总结对比

特性

非命令模式实现

命令模式实现

耦合性

调用者直接依赖 PhoneInventory 方法

调用者依赖抽象的 Command 接口

扩展性

需修改调用方代码

通过新增 Command 子类扩展

撤销支持

需自行维护操作历史

内置 undo() 方法实现撤销

灵活性

无法实现队列、延迟执行等高级功能

通过命令队列支持批量处理、延迟执行

实际意义

命令模式通过 封装请求为对象,将操作的具体实现与调用者解耦,从而解决了上述问题。对于需要支持复杂操作管理(如撤销、重做、事务)的场景,命令模式是更优的设计选择。而对于简单的一次性操作,直接调用可能更高效。


http://www.kler.cn/a/595780.html

相关文章:

  • Swoole HTTP 服务中 同步、异步 和 协程 三种模式的对比
  • 【linux】服务器限制客户端ssh访问
  • 课程5. 迁移学习
  • 【计算机网络】网络简介
  • 决策树调参技巧
  • 【STM32实物】基于STM32的太阳能充电宝设计
  • stm32 2.0.1
  • 六十天前端强化训练之第二十五天之组件生命周期大师级详解(Vue3 Composition API 版)
  • Webpack 中动态导入(Dynamic Import)的几种典型用法
  • <项目> 主从Reactor模型的高并发服务器
  • UI前端与数字孪生:打造智慧城市的双引擎
  • Android Compose 切换按钮深度剖析:从源码到实践(六)
  • 《概率论与数理统计》期末复习笔记_上
  • ocp考试有判断题吗?多少分及格?
  • 回归任务训练--MNIST全连接神经网络(Mnist_NN)
  • 构建教育类小程序:核心功能详解
  • SpringBoot @Scheduled注解详解
  • 浏览器工作原理深度解析(阶段二):HTML 解析与 DOM 树构建
  • Json的应用实例——cad 二次开发c#
  • 最近比突出的DeepSeek与ChatGPT的详细比较分析