定義
將請求封裝成對象,這可以讓你使用不同的請求,隊列,或者日志請求來參數(shù)化其他對象。命令模式也可以支持撤銷操作。
例子
現(xiàn)在需要做一個遙控器,遙控器上有很多按鈕,每一個按鈕對應(yīng)一個功能,比如開燈,開電視等。
注: 需要考慮遙控器未來可能發(fā)生的擴展與變化。
分析
我們可能有以下思路:
1.創(chuàng)建一個遙控器對象,這個對象有多個按鈕,每個按鈕都綁定一個方法,對于每一個按鈕按下的時候,調(diào)用綁定的方法即可。
- (void)actionOfButton1 {
light = new Light();
light.on();
}
- (void)actionOfButton2 {
tv = new TV();
tv.on();
}
...
分析:這種做法直觀有效。在一些場景下,也是不錯的方法。其實沒有必要盲目使用設(shè)計模式,有些場景,用簡單直觀的方式完成需求,也沒什么問題,主要是要根據(jù)實際需求來使用?;氐竭@個例子,遙控器在以后版本的迭代中,很可能不斷增加按鈕,可能交互按鈕的功能,比如把第二個按鈕的功能放到第一個按鈕等。這些情況,我們就需要不斷修改遙控器對象,這會造成潛在的錯誤。
注意:這里需要注意設(shè)計模式中兩個重要的設(shè)計原則:
類應(yīng)該對擴展開放,對修改關(guān)閉。
為交互對象之間的松耦合設(shè)計而努力。
這里擴展和修改的意思,我的理解是修改是更改了已有方法的邏輯,而擴展是指增加了新的方法。
2.看到上面的問題,進一步的想法, 不再將遙控器的按鈕方法寫死,而是提供一個接口setAction來配置:
remoteControl = new RemoteControl();
remoteControl.setAction(button1, action1);
分析:和方案一相比,我們只是將按鈕操作的內(nèi)容提取為一個對象,然后作為參數(shù)傳遞給遙控器。如果增加按鈕,也只需要新建新的動作,然后調(diào)用setAction到某一個按鈕。如果需要交互兩個按鈕的功能,直接通過setAction就可以了。
不知不覺中,我們就用到了命令模式。我們將發(fā)出請求的接受者(light,tv等)和執(zhí)行動作(on, off等)封裝為一個命令對象(即上文中action對象)。實際上,這是個“聰明”命令對象,很多場景下,這么設(shè)計也完全ok。只是調(diào)用者和接收者的解耦程度不如“傻瓜”命令對象。所謂“傻瓜”命令對象,它只調(diào)用接受者的方法,不關(guān)心方法的細節(jié)。其實主要區(qū)別就在于是否將實際工作委托給接受者?!吧倒稀泵顚ο筮€可以不關(guān)注接受者,將接受者作為參數(shù)傳遞,接受者只需要實現(xiàn)一個約定的接口。
Coding time

實現(xiàn)命令接口
接口是指一系列方法的聲明,這些方法不用具體實現(xiàn),可以在不同的類中有不同的實現(xiàn)。在這里,我們需要定義一個命令接口,接口需要實現(xiàn)execute的方法:
@protocol CommandInterface <NSObject>
@required
- (void)execute;
@end
實現(xiàn)命令
一個具體的命令需要實現(xiàn)命令接口:
@class Light;
@interface LightOnCommand : NSObject<CommandInterface>
@end
@implementation LightOnCommand
- (void)execute {
[_light on];
}
@end
實現(xiàn)控制器
@implementation SimpleRemoteControl
- (void)setCommand:(id<CommandInterface>)command {
_slot = command;
}
- (void)buttonWasPressed {
if ([_slot respondsToSelector:@selector(execute)]) {
[_slot execute];
}
}
@end
使用
Light *light = [Light new];
LightOnCommand *lightOnCommand = [[LightOnCommand alloc] initWithLight:light];
SimpleRemoteControl *control = [SimpleRemoteControl new];
[control setCommand:lightOnCommand];
[control buttonWasPressed];
這就完成了一個簡單的命令模式,可以嘗試著增加新的命令,會發(fā)現(xiàn)這樣是符合修改擴展原則的
總結(jié)
- 命令模式將發(fā)出請求的對象和執(zhí)行請求的對象解耦
- 在被解耦的兩者之間是通過命令對象進行溝通的。命令對象封裝了接受者和一個或一組動作
- 調(diào)用者通過調(diào)用命令對象的execute發(fā)出請求,這會使得接受者的動作被調(diào)用
- 調(diào)用者可以接受命令當(dāng)做參數(shù),甚至在運行時動態(tài)的進行
- 命令對象可以支持撤銷,做法是實現(xiàn)一個undo方法來回到execute執(zhí)行前的狀態(tài)
- 實際操作時,很常見使用“聰明”命令對象,也就是直接實現(xiàn)了請求,而不是將工作委托給接收者
- 命令也可以用來實現(xiàn)日志和事務(wù)系統(tǒng)