一,命令模式的實現:
命令模式里邊一般都有以下幾個角色:客戶端,請求者,命令接口,命令實現,接受者,
下邊是簡單命令模式的實現代碼實現:
1
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 receiver
30
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 receiver
39
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
}
二,命令模式的功能,好處,或者說為什么使用命令模式?
上邊的代碼是否看起來很傻呢,本來可以這樣簡單實現的:
1
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
1
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
//按照自定義優先級,排序取出cmd
21
}
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,需求:有個固定命令組合會多次被執行
解決:加入合成模式,實現方法如下,定義一個宏命令類:
1
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
解決:加入備忘錄模式,一個簡單的實現如下
1
public class ConcreteCommandOne implements Command
{
2
private Receiver receiver
3
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 :電視機
最后說一句,并不是全部按照模式寫一定就好,應該根據你的需求來應用,或者全部應用,或者部分應用,或者根本不用。