命令模式

《大话设计模式》第23章读书笔记,介绍命令模式

Posted by GrayWind on November 20, 2018

命令模式(Command)将一个请求封装为一个对象,从而使得你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

command

Command类,用来声明执行操作的接口

public interface Command {
	public void execute();
}

ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute

public class ConcreteCommand implements Command {
	private Receiver receiver;
	
	public ConcreteCommand(Receiver receiver) {
		this.receiver = receiver;
	}
	
	@Override
	public void execute() {
		receiver.action();
	}

}

Invoker类,要求该命令执行这个请求

public class Invoker {
	private Command command;

	public void setCommand(Command command) {
		this.command = command;
	}
	
	public void executeCommand() {
		command.execute();
	}
}

Receiver类,知道如何实施与执行一个请求相关的操作,任何类都可能作为一个接收者

public class Receiver {
	public void action() {
		System.out.println("执行请求!");
	}
}

客户端代码,创建一个具体命令对象并设定它的接收者

public class CommandTest {
	public static void main(String[] args) {
		Receiver r = new Receiver();
        Command c = new ConcreteCommand(r);
        Invoker i = new Invoker();

        // Set and execute command 
        i.setCommand(c);
        i.executeCommand();//执行请求!
	}
}

命令模式的优点:

  1. 能较容易地设计一个命令队列
  2. 在需要的情况下,可以较容易地将命令记入日志
  3. 允许接收请求的一方决定是否要否决请求
  4. 可以容易地实现对请求的撤销和重做
  5. 由于新加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易
  6. 命令模式把请求一个操作的对象与指导怎么执行一个操作的对象分割开

对于不清楚是否需要撤销/恢复功能时,不应该急于使用命令模式。敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。


实例引入,顾客去烧烤店,通过服务员点菜,服务员根据店里的食材情况决定是接收命令并通知厨师,还是拒接命令。同时,要记录点菜的日志,能够进行加菜和取消。

抽象命令类保持不变。

具体命令类,除了执行方法外,还重写了toString()方法便于后面判断使用

public class BakeChickenWingCommand implements Command {
	private Barbecuer receiver;
	
	public BakeChickenWingCommand(Barbecuer receiver) {
		this.receiver = receiver;
	}
	
	@Override
	public void execute() {
		receiver.bakeChickenWing();
	}
	
	@Override
	public String toString() {
		return "烤鸡翅";
	}
}

public class BakeMuttonCommand implements Command {
	private Barbecuer receiver;
	
	public BakeMuttonCommand(Barbecuer receiver) {
		this.receiver = receiver;
	}
	
	@Override
	public void execute() {
		receiver.bakeMutton();
	}

	@Override
	public String toString() {
		return "烤羊肉串";
	}
}

服务员类,不管用户想要什么烤肉,都是命令,只管记录订单,然后根据情况通知厨师

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() + "  时间:" + getTime());
		}
	}

	// 取消订单
	public void cancelOrder(Command command) {
		orders.remove(command);
		System.out.println("取消订单:" + command.toString() + "  时间:" + getTime());
	}

	// 通知全部执行
	public void notifyReceiver() {
		for (Command cmd : orders) {
			cmd.execute();
		}
	}

	private String getTime() {
		SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String TimeString = time.format(new Date());
		return TimeString;
	}
}

厨师类

public class Barbecuer {
	public void bakeMutton() {
		System.out.println("烤羊肉串!");
	}

	public void bakeChickenWing() {
		System.out.println("烤鸡翅!");
	}
}

客户端代码

public class Test {
	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.notifyReceiver();
	}
}

执行结果

增加订单:烤羊肉串  时间:2018-11-20 13:32:42
增加订单:烤羊肉串  时间:2018-11-20 13:32:42
服务员:鸡翅没有了,请点别的烧烤。
烤羊肉串!
烤羊肉串!