概念
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
命令模式调用顺序
代码规范
package com.lzhsite.technology.designPattern.command.GeneralDemo; /** * 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。 * @author lzhcode * */ public class ClientForGeneralDemo { public static void main(String[] args) { Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoker invoker = new Invoker(); invoker.setCommand(command); } }
package com.lzhsite.technology.designPattern.command.GeneralDemo; public interface Command { public void execute(); }
package com.lzhsite.technology.designPattern.command.GeneralDemo; public class ConcreteCommand implements Command { private Receiver receiver = null; private String state; public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } public void execute() { receiver.action(); } }
package com.lzhsite.technology.designPattern.command.GeneralDemo; public class Invoker { private Command command = null; public void setCommand(Command command) { this.command = command; } public void runCommand() { command.execute(); } }
package com.lzhsite.technology.designPattern.command.GeneralDemo; public class Receiver { public void action() { } }
适用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队和执行请求。
3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4.系统需要将一组操作组合在一起,即支持宏命令。
优点
1.降低对象之间的耦合度。
2.新的命令可以很容易地加入到系统中。
3.可以比较容易地设计一个组合命令。
4.调用同一方法实现不同的功能
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
实际应用
点餐问题
定义命令接口
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public interface ICommand { public void execute(); public void setCook(ICook cook); public int getTableNumber(); }
某道菜命令
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class PorkCommand implements ICommand { private int tableNumber; private ICook cook = null; public PorkCommand(int tableNumber) { this.tableNumber = tableNumber; } public void setCook(ICook cook) { this.cook = cook; } public int getTableNumber() { return this.tableNumber; } public void execute() { this.cook.cook(tableNumber, "蒜泥白肉"); } }
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class ChopCommand implements ICommand { private int tableNumber; private ICook cook = null; public ChopCommand(int tableNumber) { this.tableNumber = tableNumber; } public void setCook(ICook cook) { this.cook = cook; } public int getTableNumber() { return this.tableNumber; } public void execute() { this.cook.cook(tableNumber, "绿豆排骨煲"); } }
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class DuckCommand implements ICommand { private int tableNumber; private ICook cook = null; public DuckCommand(int tableNumber) { this.tableNumber = tableNumber; } public void setCook(ICook cook) { this.cook = cook; } public int getTableNumber() { return this.tableNumber; } public void execute() { this.cook.cook(tableNumber, "北京烤鸭"); } }
点菜命令队列
package com.lzhsite.technology.designPattern.command.RestaurantDemo; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; /** * 订单列表 * * @author lzhcode * */ public class CommandQueue { private static Queue<ICommand> commands = new ArrayBlockingQueue<ICommand>(200); public static void addMenu(MenuCommand menuCommand) { for (ICommand command : menuCommand.getCommands()) { commands.add(command); } } public static void removeOneCommand() { if (commands.size() > 0) { commands.poll(); } } public static ICommand getOneCommand() { ICommand command = null; if (commands.size() > 0) { command = commands.peek(); } return command; } }
订单命令
package com.lzhsite.technology.designPattern.command.RestaurantDemo; import java.util.Collection; import java.util.concurrent.ArrayBlockingQueue; public class MenuCommand implements ICommand { private Collection<ICommand> commands = new ArrayBlockingQueue<ICommand>(100); public void addCommand(ICommand command) { commands.add(command); } public void execute() { CommandQueue.addMenu(this); } public void setCook(ICook cook) { } public int getTableNumber() { return 0; } public Collection<ICommand> getCommands() { return this.commands; } }
定义菜类型接口
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public interface ICook { public void cook(int tableNumber, String name); }
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class HotCook implements ICook, Runnable { private String name; public HotCook(String name) { this.name = name; } public void cook(int tableNumber, String name) { int cookTime = (int) (20 * Math.random()); System.out.println(this.name + " 热菜厨师正在为 " + tableNumber + " 号桌做:" + name); try { Thread.sleep(cookTime); } catch (InterruptedException exception) { exception.printStackTrace(); } System.out.println(this.name + " 热菜厨师为 " + tableNumber + " 号桌做好了:" + name + ", 共计耗时 = " + cookTime + " 秒"); } public void run() { while (true) { synchronized (ClientForRestaurantDemo.Lock) { ICommand command = CommandQueue.getOneCommand(); if (command != null) { if(command instanceof DuckCommand || command instanceof ChopCommand){ command.setCook(this); command.execute(); CommandQueue.removeOneCommand(); } } } try { Thread.sleep(1000l); } catch (InterruptedException exception) { exception.printStackTrace(); } } } }
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class ColdCook implements ICook, Runnable { private String name; public ColdCook(String name) { this.name = name; } public void cook(int tableNumber, String name) { int cookTime = (int) (20 * Math.random()); System.out.println(this.name + " 冷菜厨师正在为 " + tableNumber + " 号桌做:" + name); try { Thread.sleep(cookTime); } catch (InterruptedException exception) { exception.printStackTrace(); } System.out.println(this.name + " 冷菜厨师为 " + tableNumber + " 号桌做好了:" + name + ", 共计耗时 = " + cookTime + " 秒"); } public void run() { while (true) { synchronized (ClientForRestaurantDemo.Lock) { ICommand command = CommandQueue.getOneCommand(); if (command != null) { if(command instanceof PorkCommand){ command.setCook(this); command.execute(); CommandQueue.removeOneCommand(); } } } try { Thread.sleep(1000l); } catch (InterruptedException exception) { exception.printStackTrace(); } } } }
定义服务员
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class Waiter { public MenuCommand menuCommand = new MenuCommand(); public void orderDish(ICommand command) { menuCommand.addCommand(command); } public void orderOver() { this.menuCommand.execute(); } }
程序启动入口
package com.lzhsite.technology.designPattern.command.RestaurantDemo; import java.util.concurrent.locks.Lock; /** * 系统需要在不同的时间指定请求、将请求排队和执行请求。 系统需要将一组操作组合在一起,即支持宏命令。 * * @author lzhcode * */ public class ClientForRestaurantDemo { public static Byte Lock = 1; public static void main(String[] args) { // 4个厨师同时开始工作 CookManager.runCookManager(); // 一次循环相当于一个订单 for (int i = 0; i < 5; i++) { Waiter waiter = new Waiter(); ICommand chopCommand = new ChopCommand(i); ICommand duckCommand = new DuckCommand(i); ICommand porkCommand = new PorkCommand(i); waiter.orderDish(chopCommand); waiter.orderDish(duckCommand); waiter.orderDish(porkCommand); waiter.orderOver(); } } }
package com.lzhsite.technology.designPattern.command.RestaurantDemo; public class CookManager { public static void runCookManager() { HotCook cook1 = new HotCook("张三"); HotCook cook2 = new HotCook("李四"); ColdCook cook3 = new ColdCook("王五"); ColdCook cook4 = new ColdCook("钱六"); Thread thread1 = new Thread(cook1); thread1.start(); Thread thread2 = new Thread(cook2); thread2.start(); Thread thread3 = new Thread(cook3); thread3.start(); Thread thread4 = new Thread(cook4); thread4.start(); } }