设计模式之命令模式

内容抄自《设计模式》清华大学出版社,2011

命令模式的动机与定义:  命令模式将请求的发送者和接收者解耦,在发送者与接收者之间引入命令对象,将发送者的
请求封装在命令对象中,再通过命令对象来调用接收者的方法。

命令模式的动机:
命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何
发送请求,而不必知道如何完成请求,例如开关和电灯,开关是请求的发送者,而电灯是请求的接收者,它们之间并
不存在直接的耦合关系,而是通过电线连接到一起,开关并不需要知道如何将请求传输给电灯,而是通过电线来完成
这项功能,可以理解为在电线中封装了开灯或者关灯请求,此时电线充当了封装请求的命令对象,不同的电线可以连
接不同的请求接收者,因此只需要更换一根电线,相同的开关即可以操纵不同的电器设备。

模式定义:
将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持
可撤销的操作,其别名为动作(Action)模式或 事务(Transaction)模式。

结构分析:

设计模式之命令模式

包含角色:
1.Command(抽象命令类)
2.ConcreteCommand(具体命令类)
3.Invoke(调用者)
4.Receiver(接收者)

模式分析:
1.Command

public interface Command{
    public void exec();
}


2.ConcreteCommand

public class ConcreteCommand implements Command{
    private Receiver receiver;
    @Overrides
    public void exec(){
        receiver.action();
    }
}


3.Invoke

public class Invoke{
    private Command command;
    public void call(){
        command.exec();
    }
}


4.Receiver

public void Receiver{
    public void action(){};
}

优点:
1.将请求者与接收者解耦,相同的请求者可以对应不同的接收者,相同的接收者也可以供不同的请求者使用,(这就是为什么不能省略接收者的原因)
两者具有良好的独立性。
2.新的命令不需要修改源代码,符合开闭
3.可以实现命令队列和组合命令,实现批处理操作
4.可以实现Undo和Redo操作

缺点: 会导致过多的具体命令类

适用环境:
1.系统需要请求者与接收者解耦,使得调用者和接收者相互独立。
2.系统需要在不同的时间指定请求,将请求排队和执行请求。
3.系统需要支持Undo和Redo
4.系统需要将一组操作组合在一起,即支持宏命令,实现命令的批处理。

扩展:
1.Undo和Redo
2.命令组合

栗子:非命令模式

public class WithoutOrder {

	//起源
	//没有任何东西,没有开关,开关空调直接拔插头,新的命令需要修改源代码
	public static void main1(String[] args) {
		AirCondition airCondition = new AirCondition();
		airCondition.open();
		airCondition.close();
	}
	
	//second
	//加入了开关控制,存在强耦合问题,如果要变换接收者,不抽象无法变更
	public static void main2(String[] args) {
		Controller controller = new Controller();
		controller.setReceiver(new AirCondition());
		controller.call();
	}
	
	static class Controller{
		//接收者
		private AirCondition receiver;
		public AirCondition getReceiver() {
			return receiver;
		}
		public void setReceiver(AirCondition receiver) {
			this.receiver = receiver;
		}
		//请求
		public void call(){
			//现在是强耦合,看上去好像没问题
			//问题描述1:如果加入业务逻辑,比如,当室内温度小于25度时不开启空调,那么要怎么写呢
			//问题描述2:如果加入业务逻辑,比如,当没有脏衣服的时候不运行洗衣机
			//问题描述3: xxxxx ~~~ 问题描述n
//			if(receiver instanceof AirCondition){
//				if(当前温度大于等于25度){
//					receiver.show();
//				}
//			}
//			else if(receiver instanceof WashingMachine){
//				if(有脏衣服){
//					receiver.show();
//				}
//			}
			//.....这里写了n多的业务逻辑判断,每调用一次方法就进行n多次判断,添加新的接收者还要修改源代码
			receiver.open();
		}
	}
	
	//接收者
	static class AirCondition{
		public void open() {
			System.out.println("空调开了");
		}
		public void close() {
			System.out.println("空调关了");
		}
	}
	
}

栗子:命令模式

public class WithOrder {

	//调用者
	static class Controller{
		//不关联接收者了,现在关联命令
		private Commond commond;
		public Commond getCommond() {
			return commond;
		}
		public void setCommond(Commond commond) {
			this.commond = commond;
		}
		public void call(){
			//没有任何业务逻辑,逻辑已经包含在具体命令中
			commond.exec();
		}
	}
	
	//接收者
	static class AirCondition{
		public void open() {
			System.out.println("空调开了");
		}
		public void close() {
			System.out.println("空调关了");
		}
	}
	
	//抽象命令类
	static interface Commond{
		public void exec();
	}
	
	//具体命令,开空调
	static class OpenAirCondition implements Commond{
		private AirCondition receiver;
		public OpenAirCondition() {
			this.receiver = new AirCondition();
		}
		@Override
		public void exec() {
			//if(当前室内温度大于25度){
				receiver.open();
			//}
		}
	}

	//具体命令,关空调
	static class CloseAirCondition implements Commond{
		private AirCondition receiver;
		public CloseAirCondition() {
			this.receiver = new AirCondition();
		}
		@Override
		public void exec() {
			//if(当前室内温度小于25度){
				receiver.close();
			//}
		}
	}
	
	public static void main(String[] args) {
		Controller controller = new Controller();
		controller.setCommond(new OpenAirCondition());
		controller.call();
		controller.setCommond(new CloseAirCondition());
		controller.call();
	}
}