Java设计模式之命令模式

打印 上一主题 下一主题

主题 964|帖子 964|积分 2892

概念和作用

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

1.需要解耦请求发送者和汲取者。
2.需要支持命令的排队、撤销/重做、日志记录。
3.需要动态指定请求或组合多个请求。
示例

  1. // 1.命令接口
  2. interface Command {
  3.     void execute();
  4.     void undo(); // 可选撤销功能
  5. }
复制代码
  1. // 2.接收者(真正执行操作的对象)
  2. class PhoneInventory {
  3.     private int stock = 10;
  4.    
  5.     public void buyPhones(int quantity) {
  6.         stock += quantity;
  7.         System.out.println("采购成功,当前库存:" + stock);
  8.     }
  9.    
  10.     public void sellPhones(int quantity) {
  11.         if(stock >= quantity) {
  12.             stock -= quantity;
  13.             System.out.println("销售成功,当前库存:" + stock);
  14.         } else {
  15.             System.out.println("库存不足,销售失败");
  16.         }
  17.     }
  18. }
复制代码
  1. // 3.具体命令:购买
  2. class BuyCommand implements Command {
  3.     private PhoneInventory inventory;
  4.     private int quantity;
  5.     public BuyCommand(PhoneInventory inventory, int quantity) {
  6.         this.inventory = inventory;
  7.         this.quantity = quantity;
  8.     }
  9.     @Override
  10.     public void execute() {
  11.         inventory.buyPhones(quantity);
  12.     }
  13.     @Override
  14.     public void undo() {
  15.         inventory.sellPhones(quantity);
  16.     }
  17. }
复制代码
  1. // 3.具体命令:销售
  2. class SellCommand implements Command {
  3.     private PhoneInventory inventory;
  4.     private int quantity;
  5.     public SellCommand(PhoneInventory inventory, int quantity) {
  6.         this.inventory = inventory;
  7.         this.quantity = quantity;
  8.     }
  9.     @Override
  10.     public void execute() {
  11.         inventory.sellPhones(quantity);
  12.     }
  13.     @Override
  14.     public void undo() {
  15.         inventory.buyPhones(quantity);
  16.     }
  17. }
复制代码
  1. // 4.调用者(触发命令的对象)
  2. class OrderProcessor {
  3.     private List<Command> commands = new ArrayList<>();
  4.     // 添加命令
  5.     public void addCommand(Command command) {
  6.         commands.add(command);
  7.     }
  8.     // 执行命令
  9.     public void processOrders() {
  10.         for(Command cmd : commands) {
  11.             cmd.execute();
  12.         }
  13.         commands.clear();
  14.     }
  15. }
复制代码
  1. // 5.客户端使用
  2. public class Main {
  3.     public static void main(String[] args) {
  4.         // 接收者
  5.         PhoneInventory inventory = new PhoneInventory();
  6.         // 调用者
  7.         OrderProcessor processor = new OrderProcessor();
  8.         
  9.         // 创建命令对象
  10.         processor.addCommand(new BuyCommand(inventory, 5));
  11.         processor.addCommand(new SellCommand(inventory, 3));
  12.         
  13.         // 统一执行命令
  14.         processor.processOrders();
  15.     }
  16. }
复制代码
优点和缺点

优点

1.解耦:调用者与具体操作解耦,调用者不需要知道具体实现。
2.可扩展:容易添加新命令,符合开闭原则。
3.组合命令:支持宏命令(组合多个命令)。
4.支持撤销:通过添加undo方法实现操作回滚。
缺点

1.类膨胀:每个命令都需要一个具体类。
2.复杂度增长:需要多层级对象协作。
不用命令模式的实现

  1. // 直接调用库存方法
  2. public class WithoutCommandPattern {
  3.     public static void main(String[] args) {
  4.         PhoneInventory inventory = new PhoneInventory();
  5.         
  6.         // 直接调用具体方法
  7.         inventory.buyPhones(5);
  8.         inventory.sellPhones(3);
  9.     }
  10. }
复制代码
缺陷

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

  1. // 不使用命令模式时
  2. public class WithoutCommandPattern {
  3.     public static void main(String[] args) {
  4.         PhoneInventory inventory = new PhoneInventory();
  5.         // 直接调用具体方法
  6.         inventory.buyPhones(5);  // 直接依赖 buyPhones 方法
  7.         inventory.sellPhones(3); // 直接依赖 sellPhones 方法
  8.     }
  9. }
复制代码
问题说明:
调用者(客户端 main 方法)直接调用 PhoneInventory 的具体方法 buyPhones 和 sellPhones。如果 PhoneInventory 的方法名或参数改变(如 buyPhones 改名为 purchasePhones),所有调用它的代码都需要修改。
2.难以扩展:新增功能需要修改现有代码。

假设需要新增一个 退货功能 returnPhones(int quantity),此时:
  1. // 非命令模式需要修改调用方
  2. public class WithoutCommandPattern {
  3.     public static void main(String[] args) {
  4.         PhoneInventory inventory = new PhoneInventory();
  5.         inventory.buyPhones(5);
  6.         inventory.sellPhones(3);
  7.         // 新增退货功能
  8.         inventory.returnPhones(2); // 必须修改客户端代码
  9.     }
  10. }
复制代码
问题说明:
调用者必须知道新方法 returnPhones 的存在并直接调用它。
违反开闭原则:扩展功能需要修改现有客户端代码。
对比命令模式:
命令模式只需新增 ReturnCommand 类,客户端通过添加命令对象即可扩展功能,无需修改现有调用逻辑。
3.无法支持撤销:需要额外维护操作汗青。

在非命令模式中,若用户想要撤销一次贩卖操作:
  1. public class WithoutCommandPattern {
  2.     public static void main(String[] args) {
  3.         PhoneInventory inventory = new PhoneInventory();
  4.         inventory.buyPhones(5);    // 初始库存 15
  5.         inventory.sellPhones(3);    // 库存 12
  6.         // 撤销销售操作(需要手动反向操作)
  7.         inventory.buyPhones(3);     // 必须自行记录操作历史并反向调用
  8.     }
  9. }
复制代码
问题说明:
调用者需自行记录每次操作的类型和参数(如贩卖了 3 台手机),并手动调用反向操作(如再采购 3 台)。
若操作汗青复杂(如涉及多个对象),维护成本极高。
对比命令模式:
命令模式通过 Command 接口的 undo() 方法,自动记录操作汗青并实现撤销。
4.缺乏灵活性:无法实现命令队列、延迟执行等高级功能。

假设需要将多个操作批量执行或延迟到特定时间执行:
  1. public class WithoutCommandPattern {
  2.     public static void main(String[] args) {
  3.         PhoneInventory inventory = new PhoneInventory();
  4.         // 直接调用方法,无法延迟执行或批量管理
  5.         inventory.buyPhones(5);
  6.         inventory.sellPhones(3);
  7.         // 若想实现延迟执行,需自行设计线程或定时任务
  8.     }
  9. }
复制代码
问题说明:
调用者直接触发方法调用,所有操作立刻执行。
若想实现命令队列、延迟执行或事件管理(如批量操作全部成功或全部失败),需额外开发复杂的逻辑。
对比命令模式:
命令模式通过 OrderProcessor 类管理命令队列,可轻松实现批量执行、延迟执行或事件回滚:
  1. // 命令模式支持延迟执行
  2. processor.addCommand(new BuyCommand(inventory, 5));
  3. processor.addCommand(new SellCommand(inventory, 3));
  4. // 在需要时统一执行
  5. processor.processOrders();
复制代码
总结对比

特性
非命令模式实现
命令模式实现
耦合性
调用者直接依赖 PhoneInventory 方法
调用者依赖抽象的 Command 接口
扩展性
需修改调用方代码
通过新增 Command 子类扩展
撤销支持
需自行维护操作汗青
内置 undo() 方法实现撤销
灵活性
无法实现队列、延迟执行等高级功能
通过命令队列支持批量处理、延迟执行
实际意义

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

半亩花草

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表