全能指揮官:玩轉(zhuǎn)JavaScript命令模式孕讳,讓代碼聽你的話匠楚!

全能指揮官:玩轉(zhuǎn)JavaScript命令模式,讓代碼聽你的話厂财!

命令模式的含義

  • 命令模式指的是一個(gè)執(zhí)行某些特定的指令芋簿。
  • 命令模式的示例 demo:
// 命令接口
class Command {
    execute() {}
}

// 具體命令:打開文檔
class OpenDocumentCommand extends Command {
    constructor(document) {
        super();
        this.document = document;
    }

    // 執(zhí)行打開文檔命令
    execute() {
        this.document.open();
    }
}

// 具體命令:保存文檔
class SaveDocumentCommand extends Command {
    constructor(document) {
        super();
        this.document = document;
    }

    // 執(zhí)行保存文檔命令
    execute() {
        this.document.save();
    }
}

// 接收者:文檔
class Document {
    open() {
        console.log("打開文檔");
    }

    save() {
        console.log("保存文檔");
    }
}

// 調(diào)用者:按鈕
class Button {
    constructor(command) {
        this.command = command;
    }

    // 點(diǎn)擊按鈕執(zhí)行命令
    click() {
        this.command.execute();
    }
}

// 使用示例
const document = new Document();

// 創(chuàng)建打開文檔命令并關(guān)聯(lián)文檔對(duì)象
const openCommand = new OpenDocumentCommand(document);
// 創(chuàng)建保存文檔命令并關(guān)聯(lián)文檔對(duì)象
const saveCommand = new SaveDocumentCommand(document);

// 創(chuàng)建按鈕并關(guān)聯(lián)打開文檔命令
const openButton = new Button(openCommand);
// 創(chuàng)建按鈕并關(guān)聯(lián)保存文檔命令
const saveButton = new Button(saveCommand);

// 點(diǎn)擊按鈕執(zhí)行命令
openButton.click(); // 打開文檔
saveButton.click(); // 保存文檔

命令模式的特點(diǎn)

  • 在命令模式中,Command 對(duì)象擁有更長的生命周期璃饱。還支持撤銷与斤,排隊(duì)等操作。而設(shè)計(jì)模式的主題總是會(huì)把不變的事物和變化的事物分離出來荚恶,命令模式也不例外撩穿。

JavaScript 中的命令模式

  • 所謂的命令模式,就是給對(duì)象的某個(gè)方法取一個(gè) execute 的名字谒撼,引入 command 對(duì)象和 receiver 這兩個(gè)無中生有的角色無非是把簡單的事情復(fù)雜化了食寡。
  • 命令模式的由來,其實(shí)是回調(diào)(callback)函數(shù)的一個(gè)面向?qū)ο蟮奶娲贰?/li>

宏命令

  • 宏命令是一組命令的集合廓潜,可通過宏命令的方式冻河,可一次執(zhí)行一批命令。

完整版demo:

// 命令接口
class Command {
    execute() {}
    undo() {}
    redo() {}
}

// 具體命令:打開文檔
class OpenDocumentCommand extends Command {
    constructor(document) {
        super();
        this.document = document;
    }

    execute() {
        this.document.open();
    }

    undo() {
        this.document.close();
    }

    redo() {
        this.execute();
    }
}

// 具體命令:保存文檔
class SaveDocumentCommand extends Command {
    constructor(document) {
        super();
        this.document = document;
    }

    execute() {
        this.document.save();
    }

    undo() {
        // 撤銷保存操作茉帅,恢復(fù)文檔到上一個(gè)保存點(diǎn)或初始狀態(tài)
        this.document.restore();
    }

    redo() {
        this.execute();
    }
}

// 接收者:文檔
class Document {
    constructor(name) {
        this.name = name;
        this.content = "";
        this.savedContent = "";
    }

    open() {
        console.log(`打開文檔:${this.name}`);
    }

    close() {
        console.log(`關(guān)閉文檔:${this.name}`);
    }

    save() {
        this.savedContent = this.content;
        console.log(`保存文檔:${this.name}`);
    }

    restore() {
        this.content = this.savedContent;
        console.log(`恢復(fù)文檔:${this.name}`);
    }

    setContent(content) {
        this.content = content;
        console.log(`設(shè)置文檔內(nèi)容:${this.name}`);
    }

    getContent() {
        return this.content;
    }
}

// 調(diào)用者:按鈕
class Button {
    constructor() {
        this.commandQueue = [];
        this.undoStack = [];
        this.redoStack = [];
    }

    // 將命令加入隊(duì)列
    addToQueue(command) {
        this.commandQueue.push(command);
    }

    // 執(zhí)行隊(duì)列中的命令
    executeQueue() {
        console.log("執(zhí)行命令隊(duì)列:");
        while (this.commandQueue.length > 0) {
            const command = this.commandQueue.shift();
            command.execute();
            this.undoStack.push(command);
        }
    }

    // 撤銷上一次執(zhí)行的命令
    undo() {
        if (this.undoStack.length === 0) {
            console.log("沒有可撤銷的命令");
            return;
        }

        const command = this.undoStack.pop();
        command.undo();
        this.redoStack.push(command);
        console.log("撤銷上一次命令");
    }

    // 重做上一次撤銷的命令
    redo() {
        if (this.redoStack.length === 0) {
            console.log("沒有可重做的命令");
            return;
        }

        const command = this.redoStack.pop();
        command.redo();
        this.undoStack.push(command);
        console.log("重做上一次撤銷的命令");
    }
}

// 使用示例
const document = new Document("example.txt");

// 創(chuàng)建按鈕
const button = new Button();

// 創(chuàng)建打開文檔命令并關(guān)聯(lián)文檔對(duì)象
const openCommand = new OpenDocumentCommand(document);
// 創(chuàng)建保存文檔命令并關(guān)聯(lián)文檔對(duì)象
const saveCommand = new SaveDocumentCommand(document);

// 將命令加入隊(duì)列
button.addToQueue(openCommand);
button.addToQueue(saveCommand);

// 執(zhí)行命令隊(duì)列
button.executeQueue();

// 撤銷命令
button.undo();

// 重做命令
button.redo();
  • 在這個(gè)示例中叨叙,我們首先定義了命令接口Command,其中包含execute堪澎、undo和redo方法擂错。然后創(chuàng)建了兩個(gè)具體的命令類OpenDocumentCommand和SaveDocumentCommand,它們分別實(shí)現(xiàn)了命令的執(zhí)行樱蛤、撤銷和重做操作钮呀。
  • 接著,我們定義了文檔類Document作為接收者昨凡,其中包含了打開文檔爽醋、關(guān)閉文檔、保存文檔和恢復(fù)文檔的操作便脊。
  • 然后蚂四,我們創(chuàng)建了調(diào)用者類Button,它包含命令隊(duì)列、撤銷棧和重做棧的管理遂赠。通過addToQueue方法將命令加入隊(duì)列久妆,executeQueue方法執(zhí)行隊(duì)列中的命令,undo方法撤銷上一次執(zhí)行的命令跷睦,redo方法重做上一次撤銷的命令筷弦。
  • 在示例的最后,我們創(chuàng)建了文檔對(duì)象抑诸、按鈕對(duì)象烂琴,并關(guān)聯(lián)了打開文檔和保存文檔命令。然后將命令加入隊(duì)列蜕乡,執(zhí)行命令隊(duì)列监右,撤銷命令,重做命令异希。

命令模式的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
    1. 解耦發(fā)送者和接收者:命令模式通過將請(qǐng)求封裝為命令對(duì)象健盒,將發(fā)送者和接收者解耦。發(fā)送者只需要知道如何觸摸命令称簿,而不需要關(guān)心具體的接收者和執(zhí)行操作扣癣。
    2. 易擴(kuò)展:由于命令模式將請(qǐng)求封裝成了獨(dú)立的命令對(duì)象,因此添加一個(gè)命令只需要實(shí)現(xiàn)一個(gè)新的命令的類憨降,不需要修改原有的代碼結(jié)構(gòu)
    3. 支持隊(duì)列化和延遲執(zhí)行:命令模式將多個(gè)命令對(duì)象組合成一個(gè)命令隊(duì)列(宏命令)父虑,實(shí)現(xiàn)批量執(zhí)行和撤銷操作。也可以實(shí)現(xiàn)延遲執(zhí)行授药,將命令對(duì)象存儲(chǔ)起來士嚎,在需要的時(shí)候在執(zhí)行。
    4. 支持撤銷和重做:通過保存命令的執(zhí)行歷史悔叽,可實(shí)現(xiàn)撤銷和重做操作莱衩,對(duì)于用戶操作的回退和恢復(fù)非常有用。
    5. 支持日志和記錄:可記錄命令的執(zhí)行日志娇澎,用于系統(tǒng)的跟蹤和調(diào)試笨蚁。
  • 缺點(diǎn):
    1. 增加了類的數(shù)量:隨著引入命令模式的增加,會(huì)導(dǎo)致類的數(shù)量增加趟庄,增加代碼的復(fù)雜性括细。
    2. 命令執(zhí)行效率降低:由于將命令模式需要封裝成對(duì)象,因此會(huì)增加一定的執(zhí)行開銷戚啥,對(duì)于性能要求較高的場景可能會(huì)有影響奋单。

命令模式的適用場景

  1. 撤銷和重做
  2. 異步任務(wù)處理:若在后臺(tái)處理數(shù)據(jù)或執(zhí)行長時(shí)間運(yùn)行的操作。
  3. 日志記錄和系統(tǒng)操作記錄
  4. 隊(duì)列和調(diào)度任務(wù):可將命令對(duì)象添加到隊(duì)列中猫十,然后按照隊(duì)列中的順序依次執(zhí)行览濒。

命令模式的最佳實(shí)踐

  1. 封裝命令:將每個(gè)操作封裝為獨(dú)立的命令對(duì)象
  2. 使用接口和抽象類:定義一個(gè)接口和抽象類來表示命令對(duì)象呆盖,以確保命令對(duì)象具有抑制的方法和屬性
  3. 參數(shù)化命令:在命令對(duì)象中傳遞參數(shù),使命令對(duì)象能夠執(zhí)行不同的操作
  4. 解耦調(diào)用者和接收者:調(diào)用者只需要知道如何觸發(fā)命令匾七,而不需要了解命令的具體實(shí)現(xiàn)絮短。
  5. 支持的撤銷操作
  6. 宏命令
  7. 單一職責(zé)原則:每個(gè)命令對(duì)象應(yīng)該只負(fù)責(zé)一個(gè)具體的操作或請(qǐng)求江兢,這樣可以確保命令對(duì)象的職責(zé)清晰昨忆,代碼結(jié)構(gòu)清晰。
  8. 代碼的靈活性和可擴(kuò)展性

特殊字符描述:

  1. 問題標(biāo)注 Q:(question)
  2. 答案標(biāo)注 R:(result)
  3. 注意事項(xiàng)標(biāo)準(zhǔn):A:(attention matters)
  4. 詳情描述標(biāo)注:D:(detail info)
  5. 總結(jié)標(biāo)注:S:(summary)
  6. 分析標(biāo)注:Ana:(analysis)
  7. 提示標(biāo)注:T:(tips)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杉允,一起剝皮案震驚了整個(gè)濱河市邑贴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叔磷,老刑警劉巖拢驾,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異改基,居然都是意外死亡繁疤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門秕狰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稠腊,“玉大人,你說我怎么就攤上這事鸣哀〖芗桑” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵我衬,是天一觀的道長叹放。 經(jīng)常有香客問我,道長挠羔,這世上最難降的妖魔是什么井仰? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮破加,結(jié)果婚禮上糕档,老公的妹妹穿的比我還像新娘。我一直安慰自己拌喉,他們只是感情好速那,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尿背,像睡著了一般端仰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上田藐,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天荔烧,我揣著相機(jī)與錄音吱七,去河邊找鬼。 笑死鹤竭,一個(gè)胖子當(dāng)著我的面吹牛踊餐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臀稚,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吝岭,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了吧寺?” 一聲冷哼從身側(cè)響起窜管,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚机,沒想到半個(gè)月后幕帆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赖条,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年失乾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纬乍。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碱茁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蕾额,到底是詐尸還是另有隱情早芭,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布诅蝶,位于F島的核電站退个,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏调炬。R本人自食惡果不足惜语盈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缰泡。 院中可真熱鬧刀荒,春花似錦、人聲如沸棘钞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宜猜。三九已至泼返,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姨拥,已是汗流浹背绅喉。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工渠鸽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柴罐。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓徽缚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親革屠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凿试,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 假設(shè)有一個(gè)快餐店,而我是該餐廳的點(diǎn)餐服務(wù)員屠阻,那么我一天的工作應(yīng)該是這樣的:當(dāng)某位客人點(diǎn)餐或者打來訂餐電話后红省,我會(huì)把...
    yufawu閱讀 687評(píng)論 1 4
  • 什么是命令模式额各? 假設(shè)有一個(gè)快餐店国觉,而我是該餐廳的點(diǎn)餐服務(wù)員,那么我一天的工作應(yīng)該是這樣的:當(dāng)某位客人點(diǎn)餐或者打來...
    27億光年中的小小塵埃閱讀 154評(píng)論 0 0
  • 場景 在飯店里吃飯經(jīng)常會(huì)出現(xiàn)上餐錯(cuò)誤的問題虾啦,比如上菜順序不對(duì)或上菜上錯(cuò)桌的情況 問題來了這種情況在編程中就是常說的...
    皆為序幕_閱讀 404評(píng)論 0 1
  • 1.初識(shí)命令模式 將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象麻诀,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志傲醉,以及...
    王偵閱讀 707評(píng)論 0 2
  • 3.5 隊(duì)列請(qǐng)求## 所謂隊(duì)列請(qǐng)求蝇闭,就是對(duì)命令對(duì)象進(jìn)行排隊(duì),組成工作隊(duì)列硬毕,然后依次取出命令對(duì)象來執(zhí)行呻引。多用多線程或...
    七寸知架構(gòu)閱讀 2,021評(píng)論 4 54