Loading... # 设计模式(二十)——命令模式 ## 参考 > 大话设计模式  ——  程杰 著 ## 目录 [设计模式(一)——简单工厂模式](https://www.princelei.club/archives/67.html) [设计模式(二)——策略模式](https://www.princelei.club/archives/68.html) [设计模式(三)——设计原则](https://www.princelei.club/archives/116.html) [设计模式(四)——装饰模式](https://www.princelei.club/archives/117.html) [设计模式(五)——代理模式](https://www.princelei.club/archives/119.html) [设计模式(六)——工厂方法模式](https://www.princelei.club/archives/132.html) [设计模式(七)——原型模式](https://www.princelei.club/archives/133.html) [设计模式(八)——模板方法模式](https://www.princelei.club/archives/134.html) [设计模式(九)——外观模式](https://www.princelei.club/archives/135.html) [设计模式(十)——建造者模式](https://www.princelei.club/archives/136.html) [设计模式(十一)——观察者模式](https://www.princelei.club/archives/137.html) [设计模式(十二)——抽象工厂模式](https://www.princelei.club/archives/138.html) [设计模式(十三)——状态模式](https://www.princelei.club/archives/139.html) [设计模式(十四)——适配器模式](https://www.princelei.club/archives/140.html) [设计模式(十五)——备忘录模式](https://www.princelei.club/archives/141.html) [设计模式(十六)——组合模式](https://www.princelei.club/archives/147.html) [设计模式(十七)——迭代器模式](https://www.princelei.club/archives/148.html) [设计模式(十八)——单例模式](https://www.princelei.club/archives/157.html) [设计模式(十九)——桥接模式](https://www.princelei.club/archives/159.html) [设计模式(二十)——命令模式](https://www.princelei.club/archives/160.html) [设计模式(二十一)——职责链模式](https://www.princelei.club/archives/161.html) [设计模式(二十二)——中介者模式](https://www.princelei.club/archives/162.html) [设计模式(二十三)——享元模式](https://www.princelei.club/archives/163.html) [设计模式(二十四)——解释器模式](https://www.princelei.club/archives/173.html) [设计模式(二十五)——访问者模式](https://www.princelei.club/archives/174.html) ## 何为命令模式 - 命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 ## 示例 命令模式实现客人点餐 ``` /** * 烤肉串者 */ public class Barbecuer { //烤羊肉 public void bakeMutton(){ System.out.println("烤羊肉串!"); } //烤鸡翅 public void bakeChickenWing(){ System.out.println("烤鸡翅!"); } } ``` ``` /** * 抽象命令 */ public interface Command { void executeCommand(); } ``` ``` /** * 服务员 */ public class Waiter { //命令容器 private List<Command> orders = new ArrayList<>(); //设置订单 public void setOrder(Command command) { //客户提出请求时,对 if (command.toString().equals("烤鸡翅")) { System.out.println("服务员:鸡翅没有了,请点别的烧烤。"); } else { orders.add(command); System.out.println("增加订单: " + command.toString() + " 时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } } //取消订单 public void cancelOrder(Command command) { orders.remove(command); System.out.println("取消订单: " + command.toString() + " 时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } //通知全部执行 public void execute() { orders.forEach(Command::executeCommand); } } ``` ``` /** * 烤羊肉串命令 */ public class BakeMuttonCommand implements Command{ private Barbecuer receiver; public BakeMuttonCommand(Barbecuer receiver){ this.receiver = receiver; } @Override public void executeCommand() { receiver.bakeMutton(); } @Override public String toString() { return "烤羊肉串"; } } ``` ``` /** * 烤鸡翅命令 */ public class BakeChickenWingCommand implements Command { private Barbecuer receiver; public BakeChickenWingCommand(Barbecuer receiver) { this.receiver = receiver; } @Override public void executeCommand() { receiver.bakeChickenWing(); } @Override public String toString() { return "烤鸡翅"; } } ``` ``` /** * 客户端 */ public class MainClass { public static void main(String[] args) { //开店前准备 Barbecuer boy = new Barbecuer(); Command bakeMuttonCommand1 = new BakeMuttonCommand(boy); Command bakeMuttonCommand2 = new BakeMuttonCommand(boy); Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy); Waiter girl = new Waiter(); //开门营业,顾客点菜 girl.setOrder(bakeMuttonCommand1); girl.setOrder(bakeMuttonCommand2); girl.setOrder(bakeChickenWingCommand1); //点菜完毕,通知厨房 girl.execute(); } } ``` 输出: ``` 增加订单: 烤羊肉串 时间:2019-05-15 16:59:27 增加订单: 烤羊肉串 时间:2019-05-15 16:59:27 服务员:鸡翅没有了,请点别的烧烤。 烤羊肉串! 烤羊肉串! ``` ## 优点 - 较容易地设计一个命令队列 - 在需要的情况下,可以较容易地将命令记入日志 - 允许接收请求的一方决定是否要否决请求 - 可以容易地实现对请求的撤销和重做 - 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易 - 把请求一个操作的对象和知道怎么执行一个操作的对象分割开 是否碰到类似情况就一定要实现命令模式呢? 比如命令模式支持撤销/恢复操作功能,但你还不清楚是否需要这个功能时,你要不要实现命令模式? ### 其实应该是不要实现。敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。 ## 撤销/重做 ``` /** * 抽象命令 */ public interface Command { public void execute(); // 执行命令和重做 public void undo(); // 执行撤销操作 } ``` ``` /** * 具体命令1 */ public class ConcreteCommand1 implements Command{ @Override public void execute() { System.out.println("command1 执行"); } @Override public void undo() { System.out.println("command1 撤销"); } } ``` ``` /** * 具体命令2 */ public class ConcreteCommand2 implements Command{ @Override public void execute() { System.out.println("command2 执行"); } @Override public void undo() { System.out.println("command2 撤销"); } } ``` ``` /** * 管理类 */ public class CommandManager { private List<Command> undoList = new ArrayList(); private List<Command> redoList = new ArrayList(); // 可撤销的步数,-1时无限步 private int undoCount = -1; public CommandManager() { // 可通过配置文件配置撤销步数 undoCount = 5; } /** * 执行新操作 */ public void executeCommand(Command cmd) { // 执行操作 cmd.execute(); undoList.add(cmd); // 保留最近undoCount次操作,删除最早操作 if (undoCount != -1 && undoList.size() > undoCount) { undoList.remove(0); } // 执行新操作后清空redoList,因为这些操作不能恢复了 redoList.clear(); } /** * 执行撤销操作 */ public void undo() { if (undoList.size() <= 0) { return; } Command cmd = undoList.get(undoList.size() - 1); cmd.undo(); undoList.remove(cmd); redoList.add(cmd); } /** * 执行重做 */ public void redo() { if (redoList.size() <= 0) { return; } Command cmd = redoList.get(redoList.size() - 1); cmd.execute(); redoList.remove(cmd); undoList.add(cmd); } } ``` ``` /** * 客户端 */ public class MainClass { public static void main(String[] args) { CommandManager manager = new CommandManager(); manager.executeCommand(new ConcreteCommand1()); manager.executeCommand(new ConcreteCommand2()); manager.undo(); manager.undo(); manager.redo(); manager.redo(); } } ``` 输出: ``` command1 执行 command2 执行 command2 撤销 command1 撤销 command1 执行 command2 执行 ``` Last modification:June 11th, 2020 at 06:21 pm © 允许规范转载
情感真挚自然,字里行间传递出强烈的感染力。