被说了很多遍的设计模式---命令模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ABCD898989/article/details/57414015

[把你的理性思维慢慢变成条件反射]

本文,我们讲介绍命令模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:

操作系统:win7 x64

其他软件:eclipse mars,jdk8

-------------------------------------------------------------------------------------------------------------------------------------

经典问题:

发布订阅功能的)转发器,功能开关,等等。

思路分析:

要点一:转发器负责命令的接受与发布,并保证命令被调用。

要点二:转发器解耦双方责任关系。具有较高的灵活性。

示例工程:


错误写法:


创建CommandExecute.java文件,具体内容如下:

package com.csdn.ingo.gof_Command;

public class CommandExecute {
	public void commandA(){
		System.out.println("command A was executed");
	}
	public void commandB(){
		System.out.println("command B was executed");
	}
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_Command;
public class Window {
	public static void main(String[] args) {
		CommandExecute cmd = new CommandExecute();
		cmd.commandA();
		cmd.commandB();
	}
}

错误原因:

客户端与被调用方的代码过于耦合,对于后期的维护与扩展极为不利。并且,客户端无法灵活的执行相关功能,即无转接器的功能。

推荐写法:


创建Command.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.one;

public abstract class Command {
	protected Receiver receiver;
	
	public Command(Receiver receiver){
		this.receiver = receiver;
	}
	public abstract void excuteCommand();
}
创建ConcreteCommandA.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one;

public class ConcreteCommandA extends Command{

	public ConcreteCommandA(Receiver receiver) {
		super(receiver);
	}

	@Override
	public void excuteCommand() {
		receiver.commandA();
	}
}
创建ConcreteCommandB.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.one;

public class ConcreteCommandB extends Command{

	public ConcreteCommandB(Receiver receiver) {
		super(receiver);
	}

	@Override
	public void excuteCommand() {
		receiver.commandB();
	}
}
创建Invoker.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.one;

public class Invoker {
	private Command command;
	public void setOrder(Command command){
		this.command = command;
	}
	public void notifya(){
		command.excuteCommand();
	}
}
创建Receiver.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.one;

public class Receiver {
	public void commandA(){
		System.out.println("command A was executed");
	}
	public void commandB(){
		System.out.println("command B was executed");
	}
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.one;

public class Window {
	public static void main(String[] args) {
		Receiver boy = new Receiver();
		
		Command cmdA = new ConcreteCommandA(boy);
		Command cmdB = new ConcreteCommandB(boy);
		
		Invoker ivk = new Invoker();
		ivk.setOrder(cmdA);
		ivk.notifya();
		ivk.setOrder(cmdB);
		ivk.notifya();
	}
}

推荐原因:

上文的推荐代码将客户端的命令在调度真正的receiver之间进行了封装。由此将命令的发起与执行进行分割。使得客户端不必知道命令的实际接口,及接口内的执行细节。每一个命令类对应一个命令处理器,对于客户端通过注入的命令的参数不同,其对应的实现亦不同。从而实现“封装”。

扩展实现:


创建Command.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

public abstract class Command {
	protected Receiver receiver;
	
	public Command(Receiver receiver){
		this.receiver = receiver;
	}
	public abstract void excuteCommand();
}
创建ConcreteCommandA.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

public class ConcreteCommandA extends Command{

	public ConcreteCommandA(Receiver receiver) {
		super(receiver);
	}

	@Override
	public void excuteCommand() {
		receiver.commandA();
	}
}
创建ConcreteCommandB.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

public class ConcreteCommandB extends Command{

	public ConcreteCommandB(Receiver receiver) {
		super(receiver);
	}

	@Override
	public void excuteCommand() {
		receiver.commandB();
	}
}
创建Invoker.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Invoker {
	private List<Command> orders = new ArrayList<Command>();
		
	private Command command;
	public void setOrder(Command command){
		if(command.getClass().getName().equals(ConcreteCommandA.class.getName())){
			System.out.println("Command A is closed");
		}else{
			orders.add(command);
			System.out.println("ADD Command B:"+command.getClass().getName()+",Time:"+new Date());
		}
	}
	public void cancelOrder(Command command){
		orders.remove(command);
		System.out.println("REMOVE Command:"+command.toString()+",Time:"+new Date());
	}
	public void notifya(){
		for(Command c:orders){
			c.excuteCommand();
		}
	}
}
创建Receiver.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

public class Receiver {
	public void commandA(){
		System.out.println("Command A was executed");
	}
	public void commandB(){
		System.out.println("Command B was executed");
	}
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_Command.two;

public class Window {
	public static void main(String[] args) {
		Receiver boy = new Receiver();
		Command cmdA = new ConcreteCommandA(boy);
		Command cmdB = new ConcreteCommandB(boy);
		
		Invoker ivk = new Invoker();
		ivk.setOrder(cmdA);
		ivk.setOrder(cmdA);
		ivk.setOrder(cmdB);
		ivk.notifya();
	}
}

特别提醒:

上文设计实现的undo操作,只能实现撤销命令,而不能实现有序撤销。举个栗子:如果客户端为计算器,那么此实现不能实现撤销运算,而是仅代表了正式提交前的删除某个子运算而已。

模式的再扩展:

在操作系统中的日志文件,当前流行的内存数据库,MQ组件,CQRS系统设计中等等,其都提供了快照备份的能力,此处功能实现方式有将每次的命令变化都写入备份文件,后续恢复时,再根据该命令的操作历史恢复到之前的状态。关于此功能的实现代码,请各位看官在参考如Redis日志文件的生成策略之后,结合实际需要,自行学习。

模式总结:

命令模式结构图:


命令模式:

讲一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。

组成部分:

Invoker(调用者):客户端通过该对象来调用命令。其在设计时,不需要确定命令接收方,而只需要与Command抽象命令类之间关联即可。在程序运行时,在调用具体的注入的对象的方法。

Command(抽象命令类):Command类一般是一个抽象类或者接口,在其中声明了用于执行请求的execute()。这些方法将实际效用接收方的相关方法。

Receiver(接收者):命令的实际执行者,负责具体业务处理操作等。

ConcreteCommand(具体命令类):其实现了Command类中声明的方法,其对应一个具体的接受方对象,并且其中包含了对接收方方法的调用过程。

反思:

应用场景:

  1. 需要将命令发起方与接收方进行关系解耦。使其具有封装性,隔离性。
  2. 需要保持Invoker始终处于生存状态,即,命令的发起方可以随时结束生命周期,而Invoker调用者,始终保持活动。
  3. 在扩展模式下,需要记录系统命令操作日志。

优点:

  1. 有效降低了命令客户端与命令接收方的耦合度。使得双方相互不存在直接关联,方便后续的维护与扩展。
  2. 对于新的命令的加入对旧的命令实现过程不存在任何影响。满足了“开闭原则”。
  3. 通过记录命令的调用过程,可以实现系统的撤销与恢复功能。

缺点:

  1. 具体的命令类需要对应一个具体的命令接收方,因此,在实际使用时,需要考虑业务复杂度,以免造成ConcreteCommand类的泛滥。

-------------------------------------------------------------------------------------------------------------------------------------

至此,被说了很多遍的设计模式---命令模式 结束


参考资料:

图书:《大话设计模式》

其他博文:http://blog.csdn.NET/lovelion/article/details/7563445


猜你喜欢

转载自blog.csdn.net/ABCD898989/article/details/57414015