設(shè)計模式(三)命令模式

今天在面試別人的過程,問到了設(shè)計模式,他說命令模式,what??!我沒有聽過,我只能強裝淡定,問了問他,回來趕緊翻翻書,補一補。

命令模式

日常背書:命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動的設(shè)計模式,它屬于行為型模式。請求以命令的形式包裹在對象中,并傳給調(diào)用對象。調(diào)用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應(yīng)的對象,該對象執(zhí)行命令。先來講個故事吧,再來說說自己的理解。

板面的故事

板面相信大家都吃過吧,今天講的是公司那邊的一家板面店, 我們皮皮家族經(jīng)常去吃,這家店中只有老板和老板娘,每次都記不住我們點了什么,也記不住點餐的順序,經(jīng)常發(fā)生的場景就是,老板端著一碗板面說兄弟的板面加腸好了,你滿面黑線的說,我點的是蓋飯。下面我們用代碼來描述下這個板面的業(yè)務(wù)模型。

板面1.0:緊耦合

板面店:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/21
 * @description
 */
public class Noodler {

    public void makeNoodle(){
        System.out.println("兄弟,你的板面加腸加蛋!");
    }

    public void makeRice(){
        System.out.println("兄弟,你的蓋飯!");
    }
}

皮皮家族來到了板面店

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/21
 * @description
 */
public class Demo {

    public static void main(String[] args){
        Noodler noodler = new Noodler();
        //皮皮家族來到了板面店,點了3個板面,2個蓋飯
        noodler.makeNoodle();
        noodler.makeRice();
        noodler.makeNoodle();
        noodler.makeRice();
        noodler.makeNoodle();
    }
}

效果:


板面1.0的效果

板面1.0,描述了目前板面店的業(yè)務(wù)模型,實現(xiàn)很簡單,但是暴露了很多問題。

從現(xiàn)實業(yè)務(wù)上來看

我們作為消費者直接與廚師(做飯也是老板和老板娘做的)交互,他們無法專注于自己的本質(zhì)工作(做飯),需要記住每個人點的東西,每個點餐的順序,就造成了目前的情況,不是看你點了什么,而是看他們做了什么,你點的東西可能已經(jīng)沒有原材料做不了了,但是也無法及時通知你,你想換一種飯,他可能無法顧及你的需求,這種不單一的職責(zé),讓他們無法同時顧及做飯與招呼客人點東西,兩邊的工作都可能做不好

從代碼上來看

這種設(shè)計,我們作為行為請求者,板面店作為行為執(zhí)行者,兩者是緊耦合,對于一些簡單的場景,這么做比較合適,請求者直接與執(zhí)行者交互,但在某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時,這種緊耦合的設(shè)計就不是很合適

板面2.0:使用命令模式為板面店解耦

我們皮皮家族一直在討論板面店的問題在,認(rèn)為關(guān)鍵點就是把招呼客人與做飯分開,在我們的代碼中,修改老板與老板娘的職責(zé),讓他們只負(fù)責(zé)做飯:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class Maker {

    public void make(String s){
        System.out.println("兄弟,我只負(fù)責(zé)做飯!我正在" + s);
    }

    //老板、老板娘查看廚房信息,看看客人點的是否能做
    public boolean getInfo(String s){
        if ("板面".equals(s)){
            return true;
        }else if ("蓋飯".equals(s)){
            return true;
        }else if ("韭菜水餃".equals(s)){
            System.out.println("韭菜沒有了,不要讓客人點韭菜水餃了!");
            return false;
        }else {
            return true;
        }
    }
}

為板面店做一個訂單系統(tǒng):

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public interface Command {

    /**
     * 訂單系統(tǒng)通知老板、老板娘做飯
     */
    void execute();

    /**
     * 獲取廚房信息
     * @return
     */
    String getMakeInfo();
}

在訂單系統(tǒng)中,添加一個板面的信息

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class NoodleCommand implements Command {

    private Maker maker;

    public NoodleCommand(Maker maker) {
        this.maker = maker;
    }

    @Override
    public void execute() {
        System.out.println("訂單系統(tǒng)通知老板,做一碗板面!");
        maker.make("做板面");
    }

    @Override
    public String getMakeInfo() {
        if (maker.getInfo("板面")){
            System.out.println("老板查看廚房信息!");
            System.out.println("發(fā)現(xiàn)有材料,可以做板面!");
            System.out.println("通過訂單系統(tǒng),通知服務(wù)員招呼客人說:可以點板面!");
            return "老板,給做一碗板面!";
        }else {
            System.out.println("沒有材料,做不了板面了!");
            return "NULL";
        }
    }
}

為板面店招聘一個服務(wù)員,專門負(fù)責(zé)招呼客人:

package edu.design.pattern.command;

import java.util.ArrayList;
import java.util.List;

/**
 * 服務(wù)員類
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class Waiter {

    //服務(wù)員控制訂單系統(tǒng)
    private List<Command> commandList = new ArrayList<>();

    //服務(wù)員招呼客人,把客人的點餐情況輸入點餐系統(tǒng)
    //今天沒有韭菜了,韭菜水餃點不了了
    public void setCommand(Command command){
        if (command.getMakeInfo().equals("NULL")){
            System.out.println("韭菜沒有了,包不了韭菜水餃了!");
        }else {
            commandList.add(command);
        }
    }

    //服務(wù)員在訂單系統(tǒng)中通知老板、老板娘做飯
    public void notifyMaker(){
        if (commandList.size() == 0){
            System.out.println("暫時沒有客人點餐!");
        }
        for (Command command : commandList){
            command.execute();
        }
    }
}

板面店重新開張,皮皮家族又來了:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class CommandDemo {

    public static void main(String[] args){
        //老板登場
        Maker maker = new Maker();
        //服務(wù)員登場,招呼客人
        Waiter waiter = new Waiter();
        //服務(wù)員查看訂單系統(tǒng)通知,是否可以點板面
        Command command = new NoodleCommand(maker);
        //服務(wù)員通過訂單系統(tǒng)點了一份板面
        waiter.setCommand(command);
        //服務(wù)員點擊確認(rèn),訂單系統(tǒng)通知老板做飯
        waiter.notifyMaker();
    }
}

效果:


板面2.0的效果

通過這次板面2.0的升級,解決了1.0中的這些問題,板面的故事,講完了,讓我們來總結(jié)一下吧。

總結(jié)

從上面的故事我們來總結(jié)一下

需要解決的問題

行為請求者與執(zhí)行者之間緊耦合的設(shè)計,對于一些簡單的場景,這么做比較合適,請求者直接與執(zhí)行者交互,但在某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時,這種緊耦合的設(shè)計就不是很合適。

解決的方式

命令模式制定了三個主要的角色:命令執(zhí)行對象receiver、 命令對象command、命令請求的入口invoker,通過請求者通過命令請求的入口把命令傳遞給接受執(zhí)行者進(jìn)行命令的執(zhí)行,來把請求→執(zhí)行的過程進(jìn)行了解耦

優(yōu)點

1、降低了系統(tǒng)耦合度
2、可以比較容易的把命令記入日志
3、允許命令接收方?jīng)Q定是否接受命令
4、新的命令可以很容易添加到系統(tǒng)中去

命令模式就為大家說到這里,歡迎大家來交流,指出文中一些說錯的地方,讓我加深認(rèn)識。
謝謝大家!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計模式基本原則 開放-封閉原則(OCP),是說軟件實體(類、模塊、函數(shù)等等)應(yīng)該可以拓展,但是不可修改。開-閉原...
    西山薄涼閱讀 4,081評論 3 14
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,130評論 2 17
  • 目錄 本文的結(jié)構(gòu)如下: 什么是命令模式 為什么要用該模式 模式的結(jié)構(gòu) 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應(yīng)用 總...
    w1992wishes閱讀 1,239評論 2 9
  • 觀察者模式:定義了一種一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象。這個主體對象在狀態(tài)發(fā)生變化時,會通...
    全棧未遂工程師閱讀 400評論 0 1
  • 很輕松的坐在那里上班,發(fā)卡或者收錢。這是份細(xì)心的工作。在工作中我不斷提醒自己要小心,細(xì)心。雖然簡單且不斷重復(fù)但是做...
    leayrainy閱讀 125評論 0 1

友情鏈接更多精彩內(nèi)容