iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 7
0
Modern Web

JS Design Pattern 系列 第 7

JS Design Pattern Day07-命令模式 Command

嗨大家好,今天是第七天,終於過了一個禮拜,每天下班運動完都覺得時間不夠用啊啊啊

今天就寫命令模式了

命令模式最常見的場景應用是:

有時候需要像某些物件發送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是什麼。此時希望用一種鬆耦合的方式來設計程式,使得請求發送者和請求接收者能夠消除彼此之間的耦合關係

用個常見的例子舉例,寫網頁的時候很可能畫面上會有很多按鈕,大型專案可能會將繪製按鈕功能與實際功能分給不同工程師來做,當繪製工程師做出按鈕時並不知道實際功能是什麼,那麼要怎麼綁定按鈕事件呢?我們可以用命令模式來試試:

首先先做畫面

var $btn1, $btn2, $btn3;
(function () {
    $btn1 = createBtn('btn1');
    $btn2 = createBtn('btn2');
    $btn3 = createBtn('btn3');

    function createBtn(id) {
        return $('<button>').attr('id', id).text(id).appendTo('body');
    }
})();

再來做一個setCommand函數,此函數要往按鈕綁定命令

var setCommand = function ($btn, command) {
    $btn.click(function () {
        command.execute();
    });
}

這時候繪製工程師任務算是結束了,剩下由實作功能工程師來完成,假設他們做了“MenuBar Refresh(選單更新)”、“submenu add(增加子選單)”、“submenu del(刪除子選單)”功能,這幾個功能分佈在MenuBar和SubMenu兩個物件中

var MenuBar = {
    refresh: function () {
        console.log('MenuBar Refresh');
    }
};

var SubMenu = {
    add: function () {
        console.log('submenu add');
    },
    del: function () {
        console.log('submenu del');
    }
};

接下來我們要將這些行為封裝在命令類別中

var RefreshMenuBarCommand = function (receiver) {
    this.receiver = receiver;
};

RefreshMenuBarCommand.prototype.execute = function () {
    this.receiver.refresh();
};

var AddSubMenuCommand = function (receiver) {
    this.receiver = receiver;
};

AddSubMenuCommand.prototype.execute = function () {
    this.receiver.add();
};

var DelSubMenuCommand = function (receiver) {
    this.receiver = receiver;
};

DelSubMenuCommand.prototype.execute = function () {
    this.receiver.del();
};

接下來產生command物件,並安裝到按鈕上

var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
var delSubMenuCommand = new DelSubMenuCommand(SubMenu);

setCommand($btn1, refreshMenuBarCommand);
setCommand($btn2, addSubMenuCommand);
setCommand($btn3, delSubMenuCommand);

以上就完成簡單的命令模式。
但是怎麼感覺這個命令模式很奇怪還要多一些command、receiver物件好像把事情復雜了,感覺有更簡單的寫法

var $btn1, $btn2, $btn3;
(function () {
    $btn1 = createBtn('btn1');
    $btn2 = createBtn('btn2');
    $btn3 = createBtn('btn3');

    function createBtn(id) {
        return $('<button>').attr('id', id).text(id).appendTo('body');
    }
})();

var bindClick = function ($btn, callback) {
    $btn.click(callback);
}

var MenuBar = {
    refresh: function () {
        console.log('MenuBar Refresh');
    }
};

var SubMenu = {
    add: function () {
        console.log('submenu add');
    },
    del: function () {
        console.log('submenu del');
    }
};

bindClick($btn1, MenuBar.refresh);
bindClick($btn2, SubMenu.add);
bindClick($btn3, SubMenu.del);

其實命令模式的由來是callback function的一個物件導向的替代品。命令模式跟策略模式一樣早已融入到javascript語言中。命令模式需要把運算區塊包裝起來四處傳遞,在Javascript語言中函數作為一個物件本身就可以達到這個效果,即使我們依然需要請求“接收者”,不一定要用物件導向的方式,閉包一樣可以做到通樣功能。

var RefreshMenuBarCommand = function (receiver) {
    return {
        execute: function () {
            receiver.refresh();
        }
    }
};

var setCommand = function ($btn, command) {
    $btn.click(function () {
        command.execute();
    });
}

var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
setCommand($btn1, refreshMenuBarCommand);

最後我們來做一個巨集命令,也就是一次執行一批命令的狀況。過程中我們也可以省略receiver直接做一個命令物件,會多做receiver本來就是為了讓物件之間解藕,現在我們省略這個過程:
首先一樣做很多個命令

var closeDoorCommand = {
    execute: function () {
        console.log('關門');
    }
};

var openPcCommand = {
    execute: function () {
        console.log('開電腦');
    }
};

var openAcCommand = {
    execute: function () {
        console.log('開冷氣');
    }
};

再來實作巨集裏面新增命令以及執行每個命令的行為

var MarcoCommand = function () {
    return {
        commandList: [],
        add: function (command) {
            this.commandList.push(command);
        },
        execute: function () {
            commandList.forEach(function (command) {
                command.execute();
            });
        }
    }
};

最後就可以直接新增命令來使用了

var marcoCommand = MarcoCommand();
marcoCommand.add(closeDoorCommand);
marcoCommand.add(openPcCommand);
marcoCommand.add(openAcCommand);
marcoCommand.execute();

以上就是命令模式啦~


上一篇
JS Design Pattern Day06-發佈/訂閱模式 Publish/Subscribe(下)
下一篇
JS Design Pattern Day08-組合模式 Composite
系列文
JS Design Pattern 30

尚未有邦友留言

立即登入留言