
命令模式:請求以命令的形式包裹在對象中,并傳給調(diào)用對象。調(diào)用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應(yīng)的對象,該對象執(zhí)行命令。
生活小栗子:客戶下單,訂單記錄了客戶購買的產(chǎn)品,倉庫根據(jù)訂單給客戶備貨。
模式特點
命令模式由三種角色構(gòu)成:
- 發(fā)布者
invoker(發(fā)出命令,調(diào)用命令對象,不知道如何執(zhí)行與誰執(zhí)行); - 接收者
receiver(提供對應(yīng)接口處理請求,不知道誰發(fā)起請求); - 命令對象
command(接收命令,調(diào)用接收者對應(yīng)接口處理發(fā)布者的請求)。

發(fā)布者 invoker 和接收者 receiver 各自獨立,將請求封裝成命令對象 command ,請求的具體執(zhí)行由命令對象 command 調(diào)用接收者 receiver 對應(yīng)接口執(zhí)行。
命令對象 command 充當(dāng)發(fā)布者 invoker 與接收者 receiver 之間的連接橋梁(中間對象介入)。實現(xiàn)發(fā)布者與接收之間的解耦,對比過程化請求調(diào)用,命令對象 command 擁有更長的生命周期,接收者 receiver 屬性方法被封裝在命令對象 command 屬性中,使得程序執(zhí)行時可任意時刻調(diào)用接收者對象 receiver 。因此 command 可對請求進(jìn)行進(jìn)一步管控處理,如實現(xiàn)延時、預(yù)定、排隊、撤銷等功能。
代碼實現(xiàn)
class Receiver { // 接收者類
execute() {
console.log('接收者執(zhí)行請求');
}
}
class Command { // 命令對象類
constructor(receiver) {
this.receiver = receiver;
}
execute () { // 調(diào)用接收者對應(yīng)接口執(zhí)行
console.log('命令對象->接收者->對應(yīng)接口執(zhí)行');
this.receiver.execute();
}
}
class Invoker { // 發(fā)布者類
constructor(command) {
this.command = command;
}
invoke() { // 發(fā)布請求,調(diào)用命令對象
console.log('發(fā)布者發(fā)布請求');
this.command.cmd();
}
}
const warehouse = new Receiver(); // 倉庫
const order = new Command(warehouse); // 訂單
const client = new Invoker(order); // 客戶
client.invoke();
/*
輸出:
發(fā)布者發(fā)布請求
命令對象->接收者->對應(yīng)接口執(zhí)行
接收者執(zhí)行請求
*/
應(yīng)用場景
有時候需要向某些對象發(fā)送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是什么。需要一種松耦合的方式來設(shè)計程序,使得發(fā)送者和接收者能夠消除彼此之間的耦合關(guān)系。
——《JavaScript 設(shè)計模式與開發(fā)實踐》
- 不關(guān)注執(zhí)行者,不關(guān)注執(zhí)行過程;
- 只要結(jié)果,支持撤銷請求、延后處理、日志記錄等。
優(yōu)缺點
- 優(yōu)點:
- 發(fā)布者與接收者實現(xiàn)解耦;
- 可擴(kuò)展命令,對請求可進(jìn)行排隊或日志記錄。(支持撤銷,隊列,宏命令等功能)。
- 缺點:
- 額外增加命令對象,非直接調(diào)用,存在一定開銷。
宏命令
宏命令:一組命令集合(命令模式與組合模式的產(chǎn)物)
發(fā)布者發(fā)布一個請求,命令對象會遍歷命令集合下的一系列子命令并執(zhí)行,完成多任務(wù)。
// 宏命令對象
class MacroCommand {
constructor() {
this.commandList = []; // 緩存子命令對象
}
add(command) { // 向緩存中添加子命令
this.commandList.push(command);
}
exceute() { // 對外命令執(zhí)行接口
// 遍歷自命令對象并執(zhí)行其 execute 方法
for (const command of this.commandList) {
command.execute();
}
}
}
const openWechat = { // 命令對象
execute: () => {
console.log('打開微信');
}
};
const openChrome = { // 命令對象
execute: () => {
console.log('打開Chrome');
}
};
const openEmail = { // 命令對象
execute: () => {
console.log('打開Email');
}
}
const macroCommand = new MacroCommand();
macroCommand.add(openWechat); // 宏命令中添加子命令
macroCommand.add(openChrome); // 宏命令中添加子命令
macroCommand.add(openEmail); // 宏命令中添加子命令
macroCommand.execute(); // 執(zhí)行宏命令
/* 輸出:
打開微信
打開Chrome
打開Email
*/
傻瓜命令與智能命令
傻瓜命令:命令對象需要接收者來執(zhí)行客戶的請求。
智能命令:命令對象直接實現(xiàn)請求,不需要接收者,“聰明”的命令對象。
“傻瓜命令” 與 “智能命令” 的區(qū)別在于是否有 “接收者” 對象。
// openWechat 是智能命令對象,并沒有傳入 receiver 接收對象
const openWechat = {
execute: () => { // 命令對象直接處理請求
console.log('打開微信');
}
};
沒有 “接收者” 的智能命令與策略模式很類似。代碼實現(xiàn)類似,區(qū)別在于實現(xiàn)目標(biāo)不同。
- 策略模式中實現(xiàn)的目標(biāo)是一致的,只是實現(xiàn)算法不同(如目標(biāo):根據(jù)KPI計算獎金);
- 智能命令的解決問題更廣,目標(biāo)更具散發(fā)性。(如目標(biāo):計算獎金/計算出勤率等)。
參考文章
本文首發(fā)Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以樂之名
本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請指明出處。