命令模式里边一般都有以下几个角色:客户端,请求者,命令接口,命令实现,接受者,
下边是简单命令模式的实现代码实现:

public class Client
{ 2

public static void main(String[] args)
{ 3
Receiver receiver = new Receiver();4
Command commandOne = new ConcreteCommandOne(receiver);5
Command commandTwo = new ConcreteCommandTwo(receiver); 6
Invoker invoker = new Invoker(commandOne,commandTwo);7
invoker.actionOne();8
invoker.actionTwo();9
} 10
}11

public class Invoker
{ 12
private Command commandOne;13
private Command commandTwo;14

public Invoker(Command commandOne,Command commandTwo)
{ 15
this.commandOne = commandOne;16
this.commandTwo = commandTwo;17
}18

public void actionOne()
{ 19
commandOne.execute();20
}21

public void actionTwo()
{ 22
commandTwo.execute();23
}24
}25

public interface Command
{ 26
void execute();27
}28

public class ConcreteCommandOne implements Command
{ 29
private Receiver receiver30

public ConcreteCommandOne(Receiver receiver)
{ 31
this.receiver = receiver;32
}33

public void execute()
{ 34
receiver.actionOne();35
}36
}37

public class ConcreteCommandTwo implements Command
{ 38
private Receiver receiver39

public ConcreteCommandTwo(Receiver receiver)
{ 40
this.receiver = receiver;41
}42

public void execute()
{ 43
receiver.actionTwo();44
}45
}46

public class Receiver
{ 47

public Receiver()
{ 48
//
49
}50

public void actionOne()
{ 51
System.out.println("ActionOne has been taken.");52
}53

public void actionTwo()
{ 54
System.out.println("ActionTwo has been taken.");55
}56
}二,命令模式的功能,好处,或者说为什么使用命令模式?
上边的代码是否看起来很傻呢,本来可以这样简单实现的:

public class Client
{ 2

public static void main(String[] args)
{ 3
Receiver receiver = new Receiver();4
receiver.actionOne();5
receiver.actionTwo();6
}7
} 8

public class Receiver
{ 9

public Receiver()
{ 10
//
11
}12

public void actionOne()
{ 13
System.out.println("ActionOne has been taken.");14
}15

public void actionTwo()
{ 16
System.out.println("ActionTwo has been taken.");17
}18
}
看多简洁,如果是像上边如此简单的需求,这个才应该是我们的选择,但是有些情况下这样的写法不能解决的,
或者说解决起来不好,所以引入命令模式.
(1)我们须要Client和Receiver同时开发,而且在开发过程中分别须要不停重购,改名
(2)如果我们要求Redo ,Undo等功能
(3)我们须要命令不按照调用执行,而是按照执行时的情况排序,执行
(4)开发后期,我们发现必须要log哪些方法执行了,如何在尽量少更改代码的情况下实现.并且渐少重复代码
(5)在上边的情况下,我们的接受者有很多,不止一个
解决办法:
情况一,我们可以定义一个接口,让Receiver实现这个接口,Client按照接口调用。
情况二,我们可以让Receiver记住一些状态,例如执行前的自己的状态,用来undo,但自己记录自己的状态
实现起来比较混乱,一般都是一个累记录另一个类的状态.
情况三,很难实现
情况四,,我们须要在每个Action,前后加上log
情况五,相对好实现,但是再加上这个,是否感觉最终的实现很混乱呢
好,我们再来看看命令模式,在命令模式中,我们增加一些过渡的类,这些类就是上边的命名接口和命令实现,
这样就很好的解决了情况一,情况二。我们再加入一个Invoker,这样情况三和情况四就比较好解决了。
如下加入Log和排序后的Invoker

public class Invoker
{ 2
private List cmdList = new ArrayList();3

public Invoker()
{ 4
}5

public add(Command command)
{ 6
cmdList.add(command);7
}8

public remove(Command command)
{ 9
cmdList.remove(command);10
}11

public void action()
{ 12
Command cmd;13

while((cmd =getCmd()) != null)
{ 14
log("begin"+cmd.getName());15
cmd.execute();16
log("end"+cmd.getName()); 17
}18
}19

public Command getCmd()
{ 20
//按照自定义优先级,排序取出cmd21
}22
}23

public class Client
{ 24

public static void main(String[] args)
{ 25
Receiver receiver = new Receiver();26
Command commandOne = new ConcreteCommandOne(receiver);27
Command commandTwo = new ConcreteCommandTwo(receiver); 28
Invoker invoker = new Invoker();29
invoker.add(commandOne);30
invoker.add(commandTwo);31
iinvoker.action();32
} 33
}
三,命令模式与其它模式的配合使用:
1,看上边的Invoker的实现是否很像代理模式呢,Invoker的这种实现其实就是一种代理模式。
2,需求:有个固定命令组合会多次被执行
解决:加入合成模式,实现方法如下,定义一个宏命令类:

public class MacroCommand implements Command
{ 2
private List cmdList = new ArrayList();3

public add(Command command)
{ 4
cmdList.add(command);5
}6

public remove(Command command)
{ 7
cmdList.remove(command);8
}9

public void execute()
{ 10
Command cmd;11

for(int i=0;i<cmdList.size();i++)
{ 12
cmd = (Command)cmdList.get(i);13
cmd.execute();14
}15
} 16
}3,需求:须要redo undo
解决:加入备忘录模式,一个简单的实现如下

public class ConcreteCommandOne implements Command
{ 2
private Receiver receiver3
private Receiver lastReceiver;4

public ConcreteCommandOne(Receiver receiver)
{ 5
this.receiver = receiver;6
}7

public void execute()
{ 8
record();9
receiver.actionOne();10
}11

public void undo()
{ 12
//恢复状态
13
}14

public void redo()
{ 15
lastReceiver.actionOne();16
//
17
}18

public record()
{ 19
//记录状态20
}21
}4,需求:命令很多类似的地方
解决:使用原型模式,利用clone
这个就不写例子了。
四,命令模式的使用场合
1,须要callback的时候,例如java awt/swing/swt中的Listening的消息方式
2,须要对请求排队执行,命令的发送者和接受者有不同对的生命周期,就是命令执行的时候,可能发出命令的
Client已经不存在了
3,须要Redo Undo等函数
4,须要log每条命令
5,须要支持transaction,封装一组数据命令的时候.
五,最后再次总结一下命令模式的优点和缺点:
优点:
降低Client和命令接受者的耦合,是命令请求和命令执行的对象分割
便于修改和扩张
便于聚合多个命令
缺点:
造成出现过多的具体命令类,太多文件。
五,一个比较有意思的例子,来说明命令模式
Client :看电视的人
Invoker :遥控器
Command :电信号
具体命令 :遥控器上的按键对应的不同的电信号
Receiver :电视机
最后说一句,并不是全部按照模式写一定就好,应该根据你的需求来应用,或者全部应用,或者部分应用,或者根本不用。
