`
lastsoul
  • 浏览: 34538 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

命令模式

 
阅读更多
定义
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
结构和说明


上面的类图,一开始我有一些疑问,不知Invoker为什么要存在。现在想通了,Client的职责只是发布命令,就不要给它增加传递命令的职责。
Command:定义命令的接口,声明执行的方法。
ConcreteCommand:
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
Receiver:
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
Invoker:
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Client:创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
认识命令模式
1:命令模式的关键
命令模式的关键之处就是把请求封装成为对象,也就是命令对象,并定义了统一的执行操作的接口,这个命令对象可以被存储、转发、记录、处理、撤销等,整个命令模式都是围绕这个对象在进行。
2:命令模式的组装和调用
在命令模式中经常会有一个命令的组装者,用它来维护命令的“虚”实现和真实实现之间的关系。如果是超级智能的命令,也就是说命令对象自己完全实现好了,不需要接收者,那就是命令模式的退化,不需要接收者,自然也不需要组装者了。
而真正的用户就是具体化请求的内容,然后提交请求进行触发就好了。真正的用户会通过invoker来触发命令。
在实际开发中,Client和Invoker可以融合在一起,由客户在使用命令模式时,先进行命令对象和接收者的组装,组装完成后,再调用命令执行请求。

3:命令模式的接收者
接收者可以是任意的类,对它没有什么特殊要求,这个对象知道如何真正执行命令的操作,执行时是从command的实现类里面转调过来。
一个接收者对象可以处理多个命令,接收者和命令之间没有约定的对应关系。接收者提供的方法个数、名称、功能和命令中的可以不一样,只要能够通过调用接收者的方法来实现命令对应的功能就可以了。
4:智能命令
在标准的命令模式里面,命令的实现类是没有真正实现命令要求的功能的,真正执行命令的功能的是接收者。
如果命令的实现对象比较智能,它自己就能真实地实现命令要求的功能,而不再需要调用接收者,那么这种情况就称为智能命令。
也可以有半智能的命令,命令对象知道部分实现,其它的还是需要调用接收者来完成,也就是说命令的功能由命令对象和接收者共同来完成。
5:发起请求的对象和真正实现的对象是解耦的 
请求究竟由谁处理,如何处理,发起请求的对象是不知道的,也就是发起请求的对象和真正实现的对象是解耦的。发起请求的对象只管发出命令,其它的就不管了。
6:命令模式的调用顺序示意图
使用命令模式的过程分成两个阶段,一个阶段是组装命令对象和接收者对象的过程,另外一个阶段是触发调用Invoker,来让命令真正执行的过程。


宏命令
简单点说就是包含多个命令的命令,是一个命令的组合。
举个例子来说吧,设想一下你去饭店吃饭的过程:


1 :宏命令在哪里?
现实中是当你你点完菜,说“点完了”的时候,服务员才会启动命令的执行,请注意,这个时候执行的就不是一个命令了,而是执行一堆命令。
描述这一堆命令的就是菜单,如果把菜单也抽象成为一个命令,就相当于一个大的命令,当客户说“点完了”的时候,就相当于触发这个大的命令,意思就是执行菜单这个命令就可以了,这个菜单命令包含多个命令对象,一个命令对象就相当于一道菜。那么这个菜单就相当于我们说的宏命令。
2:如何实现宏命令
宏命令从本质上讲类似于一个命令,基本上把它当命令对象进行处理。但是它跟普通的命令对象又有些不一样,就是宏命令包含有多个普通的命令对象,执行一个宏命令,简单点说,就是执行宏命令里面所包含的所有命令对象,有点打包执行的意味。

队列请求
所谓队列请求,就是对命令对象进行排队,组成工作队列,然后依次取出命令对象来执行。多用多线程或者线程池来进行命令队列的处理,当然也可以不用多线程,就是一个线程,一个命令一个命令的循环处理,就是慢点。


日志请求
所谓日志请求,就是把请求的历史记录保存下来,一般是采用永久存储的方式。如果运行请求的过程中,系统崩溃了,那么在系统再次运行时,就可以从保存的历史记录里面获取日志请求,并重新执行命令。
日志请求的实现有两种方案,一种就是直接使用Java中的序列化方法,另外一种就是在命令对象里面添加上存储和装载的方法,其实就是让命令对象自己实现类似序列化的功能。当然要简单就直接使用Java中的序列化 。

命令模式的优缺点
1:更松散的耦合
2:更动态的控制
3:能很自然的复合命令
4:更好的扩展性


思考命令模式
命令模式的本质 
命令模式的本质是: 封装请求
何时选用命令模式
1:如果需要抽象出需要执行的动作,并参数化这些对象,可以选用命令模式,把这些需要执行的动作抽象成为命令,然后实现命令的参数化配置
2:如果需要在不同的时刻指定、排列和执行请求,可以选用命令模式,把这些请求封装成为命令对象,然后实现把请求队列化
3:如果需要支持取消操作,可以选用命令模式,通过管理命令对象,能很容易的实现命令的恢复和重做的功能
4:如果需要支持当系统崩溃时,能把对系统的操作功能重新执行一遍,可以选用命令模式,把这些操作功能的请求封装成命令对象,然后实现日志命令,就可以在系统恢复回来后,通过日志获取命令列表,从而重新执行一遍功能
5:在需要事务的系统中,可以选用命令模式,命令模式提供了对事务进行建模的方法,命令模式有一个别名就是Transaction。

退化的命令模式
前面讲到了智能命令,如果命令的实现对象超级智能,实现了命令所要求的功能,那么就不需要接收者了,既然没有了接收者,那么也就不需要组装者了。

要点总结
1、命令模式将发出请求的对象和执行请求的对象解耦。

2、在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。

3、调用者通过调用命令对象的execute()方法发出请求,这会使得接收者的动作被调用。

4、调用者可以接收命令当做参数,甚至在运行时动态地进行。

5、命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态。

6、宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销。

7、实际操作时,很常见使用“聪明”命令对象,也就是直接实现了请求,而不是将工作委托给接收者。

8、命令也可以用来实现日志和事务系统。
好处:
  ◆很容易构造一个命令队列
  ◆记录相关的命令日志
  ◆增加命令的状态,实现命令的撤销和重做
  ◆允许接受请求的一方决定是否可做
  ◆新的命令轻而易举可以加入其中
  缺点:可能会有过多的具体命令类存在
代码实例:
接受者
/**
 * 接受者:具体执行命令的人
 * @author hnylchf
 *
 */
public class Receiver {

	public void sendMessage(){
		System.out.println("保证完成任务");
	}
}

命令
package org.zhy.command;

/**
 * 命令,封装请求的方法
 * @author hnylchf
 *
 */
public interface Command {

	public void exceute();
}


请求者
package org.zhy.command;

/**
 * 请求者
 * 
 * @author hnylchf
 * 
 */
public class Invoker {

	Command command;

	public void startExceuteCommand(Command command) {
		this.command = command;
		command.exceute();
	}


}


具体命令
package org.zhy.command;

/**
 * 具体命令
 * 
 * @author hnylchf
 * 
 */
public class ConcreteCommand {

	Receiver receiver;

	public ConcreteCommand(Receiver receiver) {
		this.receiver = receiver;
	}

	public void execute() {
		receiver.sendMessage();
	}
}

演示:
package org.zhy.command;

public class Appliction {

	public static void main(String[] args) {
		Receiver receiver = new Receiver();  //执行者
		Command command = new ConcreteCommand(receiver); //命令和具体命令
		Invoker invoker = new Invoker(); //请求者
		invoker.startExceuteCommand(command); //请求者下达命令
	}
}


命令接口的撤销方法
命令接口中规定了用来封装请求的方法比如上例的execute方法,命令接口中还可以提供用来封装撤销请求的方法。既撤销方法的执行能撤销execute方法的执行效果。如果execute方法的执行效果不可撤销,那么具体命令就不必实现撤销方法。


以下示例一个关于撤销方法的demo


命令:
package org.zhy.commandundo;

public interface Command {

	public void execute(String name);
	
	public void undo();
}


具体命令:
package org.zhy.commandundo;

import java.util.ArrayList;

public class ConcreteCommand implements Command {

	ArrayList<String> dirNameList;
	MakeDir dir;

	public ConcreteCommand(MakeDir dir) {
		dirNameList = new ArrayList<String>();
		this.dir = dir;
	}

	@Override
	public void execute(String name) {
		dir.createDir(name);
		dirNameList.add(name);  //记录添加到list中
	}

	@Override
	public void undo() {
		if (dirNameList != null && dirNameList.size() > 0) {
			String str = dirNameList.get(dirNameList.size() - 1);
			dir.deleteDir(str);
			dirNameList.remove(dirNameList.size() - 1);
		} else {
			System.out.println("没有需要操作的记录");
		}

	}
}


执行者:
package org.zhy.commandundo;

import java.io.File;

public class MakeDir {

	public void createDir(String name) {
		File file = new File(name);
		file.mkdir();
	}

	public void deleteDir(String name) {
		File file = new File(name);
		file.delete();
	}
}


请求者:
package org.zhy.commandundo;

public class RequestMakedir {

	Command command;

	public RequestMakedir(Command command) {
		this.command = command;
	}

	public void startExecuteCommand(String name) {
		command.execute(name);
	}

	public void undoExecuteCommand() {
		command.undo();
	}
}


演示:

package org.zhy.commandundo;

public class Appliction {

	public static void main(String[] args) {
		
		MakeDir dir = new MakeDir();
		Command command = new ConcreteCommand(dir);
		RequestMakedir requestMakedir = new RequestMakedir(command);
		requestMakedir.startExecuteCommand("jiafei");
		requestMakedir.startExecuteCommand("jiafei2");
		requestMakedir.startExecuteCommand("jiafei3");
		requestMakedir.undoExecuteCommand();
	}
}


宏命令:
宏命令也是一个具体的命令,只不过他包含了其他命令的引用。当一个宏命令执行其execute方法时,将导致所引用的其他命令执行,因此执行一个宏命令相当于执行了多具体命令。


请求者输入英文字母或1-n之间的偶数可以请求既输出英文字母表,又输出1-n之间的偶数。
首先设计两个接收者,一个是输出英文字母表,一个是输出1-n之间的偶数
接收者1:打印英文字母
package org.zhy.commandmacro;

public class PrintLetter {

	public void printEnglish() {
		for (char c = 'a'; c < 'z'; c++) {
			System.out.print(" " + c);
		}
	}
}


接受者2:打印偶数
package org.zhy.commandmacro;

public class PrintNumber {

	int n;

	public PrintNumber(int n) {
		this.n = n;
	}

	public void printEventNumber() {
		for (int i = 0; i <= n; i++) {
			if (i % 2 == 0) {
				System.out.print(" " + i);
			}
		}
	}
}


命令:
package org.zhy.commandmacro;

public interface Command {

	public void execute();
}


命令1:处理打印字母
package org.zhy.commandmacro;

public class PrintEnglishCommand implements Command {

	PrintLetter letter;

	public PrintEnglishCommand(PrintLetter letter) {
		this.letter = letter;
	}

	@Override
	public void execute() {
		letter.printEnglish();
	}
}


命令2:处理打印偶数
package org.zhy.commandmacro;

public class PrintNumberCommand implements Command {

	PrintNumber number;

	public PrintNumberCommand(PrintNumber number) {
		this.number = number;

	}

	@Override
	public void execute() {
		number.printEventNumber();
	}
}


命令3:宏命令处理一批命令(打印字母和打印偶数)
package org.zhy.commandmacro;

import java.util.ArrayList;

public class MacroCommand implements Command {

	ArrayList<Command> commandList;

	public MacroCommand(ArrayList<Command> commandList) {
		this.commandList = commandList;
	}

	@Override
	public void execute() {
		for (Command command : commandList) {
			command.execute();
		}
	}

}


请求者:
package org.zhy.commandmacro;

public class RequestPerson {

	Command command;

	public Command getCommand() {
		return command;
	}

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

	public void startExecuteCommand() {
		command.execute();
	}
}


演示:
package org.zhy.commandmacro;

import java.util.ArrayList;

public class Appliction {

	public static void main(String[] args) {
		ArrayList<Command> commandList = new ArrayList<Command>();
		RequestPerson person = new RequestPerson();
		Command englishCommand = new PrintEnglishCommand(new PrintLetter());
		Command numberCommand = new PrintNumberCommand(new PrintNumber(10));
		commandList.add(englishCommand);
		commandList.add(numberCommand);
		Command macroCommand = new MacroCommand(commandList);
		person.setCommand(macroCommand);
		person.startExecuteCommand();
		
		
	}
}


命令者模式的优点
1、在命令者模式中,请求者不直接与接受者互交,既请求者不包含接受者的引用,因此彻底消除了彼此之间的耦合。
2、命令者模式满足了软件的“开-闭原则”。如果增加新的具体命令和该命令的接受者,不必修改调用者的代码,调用者就可以直接使用新的命令对象。反之如果增加新的调用者,不必修改现有的具体命令和接受者。新增加的调用者就可以使用已有的具体命令
3、由于请求者的请求被封装到了具体命令中,那么就可以将具体命令保存到持久化媒介中,在需要的时候重新执行这个具体命令。因此使用命令者模式可以记录日志
4、使用命令者模式可以对请求者的请求进行排队,每个请求者各自对应一个具体命令,因此可以按一定的顺序执行这些命令。
适用命令者模式的场景
1、程序需要在不同的时刻指定,排列和执行请求
2、程序需要提供撤销操作
3、程序需要支持宏操作
  • 大小: 29.6 KB
  • 大小: 24.6 KB
  • 大小: 30.2 KB
分享到:
评论

相关推荐

    设计模式之命令模式(Command)

    命令模式是一种行为设计模式,它的主要目的是将请求者与接收者解耦,使得请求的发起者无需知道哪个对象会执行这个请求,同时也让系统更容易扩展。这种模式在软件工程中广泛应用,特别是在事件驱动和分布式系统中。 ...

    设计模式之命令模式源码示例

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在软件工程中,这种模式广泛应用在各种场景中,以提高代码的灵活...

    设计模式-命令模式

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在iOS开发中,命令模式尤其适用于处理事件驱动的编程场景,因为...

    设计模式之命令模式

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在软件工程中,这种模式常用于解耦发出命令的对象和执行命令的...

    命令模式.zip

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在C#编程中,命令模式常用于实现解耦发送者和接收者,提高代码的...

    命令模式实现简单计算器功能包括撤销

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求以便支持可撤消的操作。在这个简单的计算器例子中,我们将探讨如何使用命令模式来实现加法、...

    记事本 使用了策略模式和命令模式

    在“记事本 使用了策略模式和命令模式”这个主题中,我们将深入探讨这两种设计模式以及它们如何应用于记事本软件的实现。 首先,让我们了解策略模式。策略模式是一种行为设计模式,它允许在运行时选择算法或策略。...

    设计模式C++学习之命令模式(Command)

    命令模式是一种行为设计模式,它将请求封装为一个对象,使得可以使用不同的请求、队列或者日志请求,也可以支持可撤销的操作。在C++中,命令模式的应用可以帮助我们更好地组织代码,提高代码的可扩展性和可维护性。...

    C#命令模式模型

    命令模式是一种行为设计模式,它将请求封装为一个对象,使得你可以参数化不同的请求,对请求排队或记录请求日志,以及支持可撤销的操作。C#作为.NET框架的主要编程语言,非常适合应用这种模式。本篇文章将深入探讨...

    设计模式之命令模式Java实现

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在Java中实现命令模式,我们可以按照以下步骤进行: 1. **定义...

    C#命令模式应用

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在C#编程中,命令模式常用于实现解耦,使得调用者与执行者之间...

    java 命令模式实例 (设计模式)

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在Java中,命令模式的应用非常广泛,尤其在需要解耦调用者和接收...

    命令模式代码示例

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在本示例中,我们将深入探讨命令模式的核心概念和实现方式。 ...

    命令模式代码demo

    命令模式是一种设计模式,它将请求封装为一个对象,从而使我们能使用不同的请求、队列或者日志请求,以及支持可撤销的操作。在本代码demo中,我们将深入探讨这一模式的实现及其应用。 命令模式的核心思想是解耦调用...

    设计模式之命令模式案例代码

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在本文中,我们将深入探讨命令模式的原理、结构和实际应用,并...

    java命令模式示例代码及类图

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在Java中,命令模式的应用非常广泛,尤其在需要解耦调用者和执行...

    设计模式--命令模式java例子

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在Java编程中,命令模式的应用广泛且实用。 命令模式的核心是将...

    命令模式 C++ 实现

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在C++中,我们可以利用面向对象的特性来有效地实现这个模式。 ...

    命令模式(多命令) - 代码(Java)

    命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在Java中实现命令模式,我们可以创建一个接口或者抽象类作为命令...

Global site tag (gtag.js) - Google Analytics