命令模式,实现了把命令调用与命令执行分开。用途广泛。
它可以批量执行命令,比如它可以用于进度条的显示。多个命令顺序执行,进度
条来表示命令的执行进度。如果每个命令都用command封装,成为命令对象
(command object),并且每个命令对象都有获取执行时间的方法,那么就可以方
便的获取整个执行过程的时间。据此就可以用进度条显示出这一系列任务的执行
进度。它可以实现重做(undo)操作。数据库事物中的rollback处理。GUI程序使用
命令模式更为常见,从中,你甚至可以看到MVC架构的缩影。
命令模式的用法多种多样,但总离不开三个角色和一个对象: 调用者、接受者、
客户端,和命令对象(command object). 调用者决定命令执行的时机,即它是命
令的发起者;它不知道命令的执行细节,只管发起。接受者是真正执行命令的对
象。命令对象是实现命令接口的对象,命令对象往往封装或引用了接收者。实际
编程中也不必拘泥于形式,命令对象和接收者也可合二为一,即这个对象既实现
了命令接口,又亲自执行了命令细节。客户端负责初始化调用者、接收者、命令
对象以及提供必要的信息(参数),一旦初始化完毕,各个对象的联系通路也就
建立起来了。
下面,我举一个经典的例子来说明调用者、接收者、客户端和命令对象的关系。
这个例子中要调用两个命令,我还会在后面给出调用一个命令的情况。
(代码来自维基)
/*the Invoker class*/
public class Switch {
private Command flipUpCommand;
private Command flipDownCommand;
public Switch(Command flipUpCmd, Command flipDownCmd) {
this.flipUpCommand = flipUpCmd;
this.flipDownCommand = flipDownCmd;
}
public void flipUp() {
flipUpCommand.execute();
}
public void flipDown() {
flipDownCommand.execute();
}
}
/*Receiver class*/
public class Light {
public Light() { }
public void turnOn() {
System.out.println("The light is on");
}
public void turnOff() {
System.out.println("The light is off");
}
}
/*the Command interface*/
public interface Command {
void execute();
}
/*the Command for turning on the light*/
public class FlipUpCommand implements Command {
private Light theLight;
public FlipUpCommand(Light light) {
this.theLight=light;
}
public void execute(){
theLight.turnOn();
}
}
/*the Command for turning off the light*/
public class FlipDownCommand implements Command {
private Light theLight;
public FlipDownCommand(Light light) {
this.theLight=light;
}
public void execute() {
theLight.turnOff();
}
}
/*The test class or client*/
public class PressSwitch {
public static void main(String[] args) {
Light lamp = new Light();
Command switchUp = new FlipUpCommand(lamp);
Command switchDown = new FlipDownCommand(lamp);
Switch s = new Switch(switchUp,switchDown);
try {
if (args[0].equalsIgnoreCase("ON")) {
s.flipUp();
} else if (args[0].equalsIgnoreCase("OFF")) {
s.flipDown();
} else {
System.out.println("Argument \"ON\" or \"OFF\" is required.");
}
} catch (Exception e){
System.out.println("Arguments required.");
}
}
}
这个例子中的调用者、接收者、客户端、命令对象由注释标明了。两个命令对象
封装、引用了同一个接收者--灯泡。调用者的视线被屏蔽了,调用者只知道开和
关,并不知道开、关的是灯还是别的什么东西。它也不关心具体细节。命令对象
如果去封装了门,那么开、关的操作对象就成了门,而不是灯。这种改变,调用
者也不知道和不关心。
"不关心"是设计模式分析中常常提的一个词。“不关心”是不需要关心,不需要
关心是因为不属于自己负责,不属于自己负责是因为安排,安排是基于分工,分
工提高了效率。而设计模式以及由此发展出的架构从技术上促成了分工。正是分
工意图,导致“不关心”词汇在设计模式中频现。如果开发者只有一人,没有
“不关心”,只能“全关心”。所以,思考设计模式时,有时要假想是好几个人
在开发,要一会儿扮作张三负责这一块,一会儿扮作李四负责那一块儿。这样更
容易理解模式。定义好接口,你做你的,我做我的,互不打扰。
在这个例子中,调用者和接收者被解耦了。
跟解耦相反的情况是调用者和接收者混在一块。那会是这番景象。
if (args[0].equalsIgnoreCase("ON")) {
lamp.turnOn();
}
if (args[0].equalsIgnoreCase("OFF")) {
lamp.turnOff();
}
这里的代码量倒是少了不少,但调用者和接收者是混在一块的。如果没有别的需
求,其实这样做没什么不好。毕竟在可读性不受影响情况下代码少一些不是坏事。
但如果需求不明确指定是开灯、还是开门,或者现在需求中虽然是开门,但以后
可能是开灯,那么混在一起的做法就不好了,一旦需求有变,调用者和接收者都
要修改代码。而分离开的好处,则起码调用者不用修改代码。如果调用者的编写
者是一个人,而接收者的编写者是另一个人,这样的改动就不会让编写调用者的
程序员受到牵连。
在上面的例子中,调用者是指这句话
Switch s = new Switch(switchUp,switchDown);
而这句话之前的初始化工作可以放到一个厂中完成或有容器自动完成。可以让它
们跟调用者不挤在一起,这样真正的把调用者和接收者隔离。
隔离的方式是用了一个中间对象叫做command object来完成。实际上,这种模式
就是MVC的雏形。web程序员都熟悉struts开源框架。该框架是典型的MVC结构,其
中的action一层,就相当于command object这一层。在web中的一个页面请求的流
转过程是这样的:
user(request)-->action-->module
这其实就是调用者-->命令对象-->接收者。
在Web开发中,用户的增删改是常见的功能。在struts框架为基础的系统中,开发
这种功能一般有两种方式,一种是在一个action中做判断,如果添加则调用添加
模块,删除则调用删除模块,这样这个action中就会有if判断语句。另一种方法
是一种操作用一个action完成,添加用AddAction,删除用DeleteAction等。推荐
后者。职责单一,代码不容易变的乱。一般来说,面向对象设计中出现很多
switch statement往往是不好的设计的表现。
command对象不仅仅是简单的封装一下接收者,就像action控制层不仅仅只是转发
一下请求一样,它还可以做点别的事。比如command模式常见的用途----实现
undo.
我们看一个用命令模式实现undo功能的代码.
代码来自:
http://twit88.com/blog/2008/01/26/design-pattern-in-java-101-command-pattern-behavioral-pattern/
引用
//先定义一个命令接口
public interface Command {
public void execute();
public void unExecute();
}
//定义一个计算器
public class Calculator {
private int current = 0;
public void operation(char operator, int operand) {
switch (operator) {
case '+':
current += operand;
break;
case '-':
current -= operand;
break;
case '*':
current *= operand;
break;
case '/':
current /= operand;
break;
}
System.out.println("Current value = "
+ current + " after " + operator + " " + operand);
}
}
//用这个命令类封装计算器
public class CalculatorCommand implements Command {
private char operator;
private int operand;
private Calculator calculator;
public CalculatorCommand(Calculator calculator,
char operator, int operand) {
this.calculator = calculator;
this.operator = operator;
this.operand = operand;
}
public char getOperator() {
return operator;
}
public void setOperator(char operator) {
this.operator = operator;
}
public int getOperand() {
return operand;
}
public void setOperand(int operand) {
this.operand = operand;
}
public void execute() {
calculator.operation(operator, operand);
}
public void unExecute() {
calculator.operation(undo(operator), operand);
}
// Private helper function
private char undo(char operator) {
char undo;
switch (operator) {
case '+':
undo = '-';
break;
case '-':
undo = '+';
break;
case '*':
undo = '/';
break;
case '/':
undo = '*';
break;
default:
undo = ' ';
break;
}
return undo;
}
}
//用户调用计算器
import java.util.ArrayList;
public class User {
private Calculator calculator = new Calculator();
private ArrayList<Command> commands =
new ArrayList<Command>();
private int current = 0;
public void redo(int levels) {
System.out.println("\n---- Redo " + levels + " levels ");
// Perform redo operations
for (int i = 0; i < levels; i++) {
if (current < commands.size()) {
Command command = commands.get(current++);
command.execute();
}
}
}
public void undo(int levels) {
System.out.println("\n---- Undo " + levels + " levels ");
// Perform undo operations
for (int i = 0; i < levels; i++) {
if (current >= 0) {
Command command = commands.get(--current);
command.unExecute();
}
}
}
public void compute(char operator, int operand) {
// Create command operation and execute it
Command command = new CalculatorCommand(
calculator, operator, operand);
command.execute();
// Add command to undo list
commands.add(command);
current++;
}
}
//客户端
public class TestCommandPattern {
public static void main(String[] args) {
// Create user and let her compute
User user = new User();
user.compute('+', 100);
user.compute('-', 50);
user.compute('*', 10);
user.compute('/', 2);
// Undo 4 commands
user.undo(4);
// Redo 3 commands
user.redo(3);
}
}
在这个例子中,CalculatorCommand对象不仅仅是把请求传递给Calculator,而是
在把请求传递给Calculator之前对请求进行加工、转置,从而实现了undo功能。
undo并不是在接收者这一层实现的,而是在命令对象中实现的。命令对象实现
undo时只是对请求进行转置,最后仍旧调用的接收者,而不是说在命令对象里面
又单独写了一整套的undo逻辑,它仅仅是对请求的处理。对应到struts中action
的编写,如果action中写了过多的业务逻辑,那就是不合适的,action是控制层,
更适合于写对请求进行处理的控制逻辑。MVC与命令模式何其像哉。
此例中user是调用者,user发出请求,请求传递给command对象,command对象再
调用 Calculator. 调用者用一个List来保存执行过的命令(就是压栈命令对象),
当需要undo时,就让命令对象出栈。然后执行命令对象的undo功能。在此过程中,
Calculator永远保持独立,并没有因为需要增加undo功能而更改自身。
数据库的rollback功能和undo功能类似,实现也类似。用类似的方法可以实现
rollback的功能。当然,专业的数据库里的rollback功能的实现可不是这么简单。
下面我再给一个例子。这个例子适用于服务端接收客户端的命令和相应参数然后
执行命令的情况。这个例子非常出色,它很好的表现出了命令模式的精髓。
代码来自:http://www.javaworld.com/javaworld/javatips/jw-javatip68.html?page=4
package pattern;
//TestTransactionCommand.java
import java.util.*;
final class CommandReceiver {
private int[] c;
private CommandArgument a;
private CommandReceiver() {
c = new int[2];
}
private static CommandReceiver cr = new CommandReceiver();
public static CommandReceiver getHandle() {
return cr;
}
public void setCommandArgument(CommandArgument a) {
this.a = a;
}
public void methAdd() {
c = a.getArguments();
System.out.println("The result is " + (c[0] + c[1]));
}
public void methSubtract() {
c = a.getArguments();
System.out.println("The result is " + (c[0] - c[1]));
}
}
interface Command{
public void execute();
}
class CommandManager {
private Command myCommand;
public CommandManager(Command myCommand) {
this.myCommand = myCommand;
}
public void runCommands() {
myCommand.execute();
}
}
class TransactionCommand implements Command {
private CommandReceiver commandreceiver;
private Vector commandnamelist, commandargumentlist;
private String commandname;
private CommandArgument commandargument;
private Command command;
public TransactionCommand() {
this(null, null);
}
public TransactionCommand(Vector commandnamelist, Vector commandargumentlist) {
this.commandnamelist = commandnamelist;
this.commandargumentlist = commandargumentlist;
commandreceiver = CommandReceiver.getHandle();
}
public void execute() {
for (int i = 0; i < commandnamelist.size(); i++) {
commandname = (String) (commandnamelist.get(i));
commandargument = (CommandArgument) ((commandargumentlist.get(i)));
commandreceiver.setCommandArgument(commandargument);
String classname = "pattern."+commandname + "Command";
try {
Class cls = Class.forName(classname);
command = (Command) cls.newInstance();
} catch (Throwable e) {
System.err.println(e);
}
command.execute();
}
}
}
class AddCommand extends TransactionCommand {
private CommandReceiver cr;
public AddCommand() {
cr = CommandReceiver.getHandle();
}
public void execute() {
cr.methAdd();
}
}
class SubtractCommand extends TransactionCommand {
private CommandReceiver cr;
public SubtractCommand() {
cr = CommandReceiver.getHandle();
}
public void execute() {
cr.methSubtract();
}
}
class CommandArgument {
private int[] args;
CommandArgument() {
args = new int[2];
}
public int[] getArguments() {
return args;
}
public void setArgument(int i1, int i2) {
args[0] = i1;
args[1] = i2;
}
}
public class TestTransactionCommand {
private Vector clist, alist;
public TestTransactionCommand() {
clist = new Vector();
alist = new Vector();
}
public void clearBuffer(Vector c, Vector a) {
clist.removeAll(c);
alist.removeAll(a);
}
public Vector getClist() {
return clist;
}
public Vector getAlist() {
return alist;
}
public static void main(String[] args) {
CommandArgument ca, ca2;
TestTransactionCommand t = new TestTransactionCommand();
ca = new CommandArgument();
ca.setArgument(2, 8);
Vector myclist = t.getClist();
Vector myalist = t.getAlist();
myclist.addElement("Add");
myalist.addElement(ca);
TransactionCommand tc = new TransactionCommand(myclist, myalist);
CommandManager cm = new CommandManager(tc);
cm.runCommands();
t.clearBuffer(myclist, myalist);
ca2 = new CommandArgument();
ca2.setArgument(5, 7);
myclist = t.getClist();
myalist = t.getAlist();
myclist.addElement("Subtract");
myalist.addElement(ca2);
myclist.addElement("Add");
myalist.addElement(ca2);
TransactionCommand tc2 = new TransactionCommand(myclist, myalist);
CommandManager cm2 = new CommandManager(tc2);
cm2.runCommands();
}
}
这个例子中有如下几个对象,
1. 接收者:CommandReceiver
2. 命令接口:Command
3. 命令对象:
1) TransactionCommand 批处理命令对象
2) AddCommand 加法命令
3) SubtractCommand 减法命令
4. CommandManager调用者
5. CommandArgument参数对象,它仅仅是个辅助类,封装参数。
6. TestTransactionCommand 客户端
该程序在客户端初始化完毕各个对象(包括封装命令、封装参数、把命令加进命令
列表等),然后只需要调用CommandManager.runCommand()命令即可。
不用命令模式,按照通常的做法,服务器端接收到命令后,会对命令类型做判断,
形式如下:
服务器端接收用户命令;
If(命令是加法) 则做加法运算;
If(命令是减法) 则做减法运算;
而现在的形式是:
服务器端接收用户命令;
执行用户命令;
这样,服务器端根本就不知道是什么命令(一般而言,如果用了if a is b,就看
做这段程序“知道”,比如if a is 小明, 意味着这段程序知道小明这个人),
而只需要执行命令,而命令的执行细节被曲折迂回的转到了接收者那里。
这个例子中的另一大特色是有两种类型的命令对象,一种是批处理命令对象,一
种是单个的命令对象。批处理命令对象接收请求序列,然后根据请求序列动态的
装配单个命令对象,接着调用该命令对象的执行命令。没有频繁的if if,整个过
程一起哈成。代码显得非常流畅。想想这样一种命令序列如果用If实现,将是怎
样的景象吧(循环中嵌入了许多if判断,这不正是很多人熟悉、厌烦的代码组织方
式吗)。
这种动态组装命令对象方式在网络应用开发中非常有用,比如一台电脑上的图形
变化,另一台电脑上的同一同一图形也跟着同样变化,就可以用这种方式实现。
网络游戏中别人控制的物体在你的电脑上的运动显示就是最为常见的一种可使用
命令模式实现的情景。
概括的说,命令模式起码有4个优点,
1. 解耦了调用者和接收者。
2. 命令对象可以动态构建,可以被当做参数传来传去。
3. 很多命令可以被组合成一个大命令(比如例子中的TransactionCommand)。
4. 因为不需要更改已有的类(指接收者),所以方便添加新命令(然后引用已有类即可)。
分享到:
相关推荐
命令模式(Command Pattern)是一种行为设计模式,它将请求封装为一个对象,使得你可以使用不同的请求、队列请求,或者支持可撤销的操作。在Java中实现命令模式,我们可以利用面向对象编程的特性来构建系统,使得...
### 命令模式(Command Pattern) #### 概述 命令模式是一种行为设计模式,它将请求封装成对象,以便使用不同的请求对客户端进行参数化。该模式有助于将发送请求的对象与执行请求的对象解耦。 #### 核心概念 在...
命令模式是一种行为设计模式,它将请求封装成独立的对象,使得可以使用不同的请求、队列请求、记录请求历史以及支持可撤销的操作。这种模式在软件工程中被广泛应用,尤其是在需要解耦请求发起者和接收者时,提高了...
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为对象,从而使您可以使用不同的请求、队列请求或日志请求,并支持可撤销操作。命令模式通常用于实现操作的解耦,使得发送者和接收者之间不直接关联...
12、命令模式COMMAND PATTERN 13、装饰模式DECORATOR PATTERN 14、迭代器模式ITERATOR PATTERN 15、组合模式COMPOSITE PATTERN 16、观察者模式OBSERVER PATTERN 17、责任链模式 18、访问者模式VISITOR PATTERN ...
用最简单的例子理解命令模式(Command Pattern) 命令模式的需求源自想通过一个指令(比如这里IControl的Execute方法)来控制多个类的多个方法,包含了几个要素: 1、命令:让类的各种方法抽象成类实现一个接口 2、...
通过命令模式,我们可以轻松地添加新的命令,只需创建新的具体命令类并实现`Command`接口即可。此外,由于命令对象存储了接收者,所以可以很容易地在不同上下文中重用命令,甚至可以在命令执行后撤销操作(通过保存...
总结来说,"CommandPattern)管理智能家电最终代码共4页.pdf.zip"提供的示例应该展示了如何利用命令模式来管理和控制智能家电系统,包括命令的创建、执行、撤销以及如何将这些概念融入到实际的软件设计中。...
命令模式是一种设计模式,它将请求封装为一个对象,使得我们可以使用不同的请求、队列请求、或者记录请求日志,也可以支持可撤销的操作。在C#中,命令模式的应用可以帮助我们更好地解耦系统中的调用者和接收者,提高...
**命令模式(Command Pattern)详解** 命令模式是一种行为设计模式,它将请求封装为一个对象,使得你可以使用不同的请求、队列或者日志请求,也可以支持可撤销的操作。在这个场景中,我们关注的是如何利用命令模式...
**命令模式(Command Pattern)详解** 命令模式是一种行为设计模式,它将请求封装为一个对象,使得你可以使用不同的请求、队列或者日志请求,也可以支持可撤销的操作。在C++中实现命令模式,可以有效地解耦调用者和...
**命令模式(Command Pattern)**是一种行为型设计模式,它将请求封装成对象,从而使你能够用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。在命令模式中,请求是以命令的形式...
命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在软件工程中,这种模式常用于解耦发出命令的对象(调用者)与...
在软件系统中,“行为请求者”与“行为实现者”通常呈现一种...[GOF《设计模式》]Command模式结构图如下:图1Command模式结构图Command模式将一个请求封装为一个对象,从而使你可以使用不同的请求对客户进行参数化。
在提供的文件列表中,`Program.cs`通常是主程序入口,`TestPatternCommand.csproj`是项目文件,`CommandPattern`可能是包含命令模式相关类的文件夹,`Properties`包含了项目的配置信息,而`TestPatternCommand.sln`...
工厂方法模式(Factory Method Pattern) 观察者模式(Observer Pattern) 建造者模式(Builder Pattern) 解释器模式(Interpreter Pattern) 命令模式(Command Pattern) 模板方法模式(Template Method Pattern) 桥接模式...
- `CommandPattern` 文件夹:可能包含了命令模式相关的类,如Command接口、ConcreteCommand类等。 - `Properties` 文件夹:存储项目的属性配置,如AssemblyInfo.cs,用于定义元数据。 在实际应用中,C#命令模式可能...
在命令模式中,调用者(Invoker)不再直接调用接收者(Receiver)的方法,而是通过一个命令对象(Command)作为中介。命令对象封装了对接收者的操作,实现了请求的接口。这样,当需要执行一个新的操作时,只需要创建...
在`CommandPattern.zip`文件中,可能包含了这个模式的示例代码,你可以进一步查看和学习这些代码,以便更好地理解和应用命令模式。记住,虽然这是一个简单的实现,但命令模式的威力在于其能够适应复杂的需求,例如在...
命令模式(Command Pattern) 15. 迭代器模式(Iterator Pattern) 16. 观察者模式(Observer Pattern) 17. 解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of ...