命令模式(封裝命令)

公告

如果您是第一次閱讀我的設(shè)計模式系列文章罗捎,建議先閱讀設(shè)計模式開篇祠汇,希望能得到您寶貴的建議。

前言

隨著上文 裝飾器模式 中的顧客Alice購買了機器人回家后畴博,他開始了機器人使用之旅瓢捉。

正文

機器人:“你好频丘,Alice!”
Alice:“你好!你叫什么泡态?”
機器人:“Alice搂漠,我是你忠臣的仆人 Samu∈蘖蓿”
Alice:“Samu状答,你有什么功能冷守?”
機器人:“我會唱歌刀崖,我也會跳舞。Alice拍摇,你只要說 唱歌+歌曲名稱或者跳舞+舞蹈名稱我就會唱歌跳舞亮钦。如果你連續(xù)的說唱歌+歌曲名稱 跳舞+舞蹈名稱,我會一邊唱歌一邊跳舞充活》淅颍”
Alice:“唱歌稻香 跳舞肚皮舞”
機器人:“對這個世界如果你有太多的抱怨”
機器人:“跌倒了 就不敢繼續(xù)往前走”
機器人:“為什么 人要這么的脆弱 墮落”
……(畫外音:曼妙的舞姿)……

程序員視角

現(xiàn)在要實現(xiàn)對機器人Samu說(發(fā)出指令) 唱歌+歌曲名稱或者跳舞+舞蹈名稱,機器人便會自動的唱歌或跳舞混卵。

代碼實現(xiàn)

定義命令的接口的目的是為了抽象類型映穗,并且將命令實現(xiàn)分離。

public interface ICommand {
    void excute();
}

內(nèi)部命令抽象對象幕随,用于提供命令的上下文蚁滋。

public abstract class Command implements ICommand {

    private String param;

    public Command(String param) {
        this.param = param;
    }

    protected String param() {
        return param;
    }
}

唱歌命令的實現(xiàn)(跳舞命令實現(xiàn)類似)


public class SongCommandImpl extends Command {

    public static final String KEY_SONG = "唱歌";

    public SongCommandImpl(String param) {
        super(param);
    }

    @Override
    public void excute() {
        System.out.println("調(diào)用指令 " + KEY_SONG + param());
    }
}

命令的接口與實現(xiàn)均已準備妥當,接下去是思考如何調(diào)用命令赘淮。
為了避免客戶端與具體的命令對象耦合辕录,所以通常建議搭配適配器模式唱歌跳舞這些指令梢卸,轉(zhuǎn)化為程序可理解的SongCommandImpl走诞、DanceCommandImpl

// 適配器對象用于適配字符串到命令的執(zhí)行接口
public class StringCommandAdapter implements ICommand {

    private String method;
    private String param;
    private HashMap<String, Command> map = new HashMap<>();

    private Command pickCommand(String method, String param) {
        Command command = null;
        if (method.startsWith(DanceCommandImpl.KEY_DANCE)) {
            command = createCommand(DanceCommandImpl.KEY_DANCE, param, DanceCommandImpl.class);
        } else if (method.startsWith(SongCommandImpl.KEY_SONG)) {
            command = createCommand(SongCommandImpl.KEY_SONG, param, SongCommandImpl.class);
        }
        return command;
    }

    private Command createCommand(String key, String param, Class<?> clazz) {
        if (map.containsKey(key)) {
            return map.get(key);
        }
        Command command = null;
        try {
            Constructor<?> constructor = clazz.getConstructor(String.class);
            command = (Command) constructor.newInstance(param);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        if (command != null) {
            map.put(key, command);
            return command;
        } else {
            throw new IllegalArgumentException("NO COMMAND CREATED!!!");
        }
    }

    public StringCommandAdapter(String method, String param) {
        this.method = method;
        this.param = param;
    }

    @Override
    public void excute() {
        System.out.println("調(diào)用指令:" + toString());
        Command command = pickCommand(this.method, this.param);
        command.excute();
    }

    @Override
    public String toString() {
        return "StringCommandAdapter{" +
                "method='" + method + '\'' +
                ", param='" + param + '\'' +
                ", map=" + map +
                '}';
    }
}

命令如何構(gòu)建已經(jīng)完畢蛤高,接著是命令如何被觸發(fā)蚣旱。這里模擬構(gòu)建機器人接收到命令在觸發(fā)

// 構(gòu)建命令管理器碑幅,命令的日志跟蹤都可以在這里實現(xiàn)。
public class CommandManager {

    public void invoke(StringCommandAdapter adapter) {
        adapter.excute();
    }

}

考慮到通常命令都是通過觀察者接收到消息后才觸發(fā)調(diào)用的塞绿,所以這里模擬了觀察者接收到消息的調(diào)用過程枕赵。

public class SamuCommandReceiver {

    private CommandManager invoke = new CommandManager();
    private Machine machine;

    public SamuCommandReceiver(Machine machine) {
        this.machine = machine;
        System.out.printf("機器人%s的接收功能正常開啟%n", machine);
    }

    public void onReceive(String command, String param) {
        System.out.printf("機器人%s接收到指令:%s,%s%n", machine, command, param);
        invoke.invoke(new StringCommandAdapter(command, param));
    }
}

客戶端的調(diào)用

    public static void main(String args[]) {


        Machine machine = new Machine("Samu");

        SamuCommandReceiver receiver = new SamuCommandReceiver(machine);

        receiver.onReceive("唱歌", "稻香");
        receiver.onReceive("跳舞", "肚皮舞");
    }

運行結(jié)果

創(chuàng)建了機器人 Samu
機器人Samu的接收功能正常開啟
機器人Samu接收到指令:唱歌位隶,稻香
調(diào)用指令:StringCommandAdapter{method='唱歌', param='稻香', map={}}
調(diào)用指令 唱歌稻香
機器人Samu接收到指令:跳舞拷窜,肚皮舞
調(diào)用指令:StringCommandAdapter{method='跳舞', param='肚皮舞', map={}}
調(diào)用指令 跳舞肚皮舞

總結(jié)

在軟件設(shè)計中,我們經(jīng)常需要向某些對象發(fā)送請求涧黄,但是并不知道請求的接收者是誰篮昧,也不知道被請求的操作是哪個,我們只需在程序運行時指定具體的請求接收者即可笋妥,此時懊昨,可以使用命令模式來進行設(shè)計,使得請求發(fā)送者與請求接收者消除彼此之間的耦合春宣,讓對象之間的調(diào)用關(guān)系更加靈活酵颁。

命令模式可以對發(fā)送者和接收者完全解耦,發(fā)送者與接收者之間沒有直接引用關(guān)系月帝,發(fā)送請求的對象只需要知道如何發(fā)送請求躏惋,而不必知道如何完成請求。這就是命令模式的模式動機嚷辅。

命令模式

在命令模式中簿姨,將一個請求封裝為一個對象,從而使我們可用不同的請求對客戶進行參數(shù)化簸搞;對請求排隊或者記錄請求日志扁位,以及支持可撤銷的操作。命令模式是一種對象行為型模式趁俊,其別名為動作模式或事務(wù)模式域仇。

命令模式包含四個角色:

  • 抽象命令類中聲明了用于執(zhí)行請求的execute()等方法,通過這些方法可以調(diào)用請求接收者的相關(guān)操作寺擂;
  • 具體命令類是抽象命令類的子類暇务,實現(xiàn)了在抽象命令類中聲明的方法,它對應(yīng)具體的接收者對象沽讹,將接收者對象的動作綁定其中般卑;
  • 調(diào)用者即請求的發(fā)送者,又稱為請求者爽雄,它通過命令對象來執(zhí)行請求蝠检;
  • 接收者執(zhí)行與請求相關(guān)的操作,它具體實現(xiàn)對請求的業(yè)務(wù)處理挚瘟。

命令模式的本質(zhì)是對命令進行封裝叹谁,將發(fā)出命令的責任和執(zhí)行命令的責任分割開饲梭。

命令模式使請求本身成為一個對象,這個對象和其他對象一樣可以被存儲和傳遞焰檩。

命令模式的主要優(yōu)點在于降低系統(tǒng)的耦合度憔涉,增加新的命令很方便,而且可以比較容易地設(shè)計一個命令隊列和宏命令析苫,并方便地實現(xiàn)對請求的撤銷和恢復兜叨;

其主要缺點在于可能會導致某些系統(tǒng)有過多的具體命令類。

命令模式適用情況包括:

  • 需要將請求調(diào)用者和請求接收者解耦衩侥,使得調(diào)用者和接收者不直接交互国旷;
  • 需要在不同的時間指定請求、將請求排隊和執(zhí)行請求茫死;
  • 需要支持命令的撤銷操作和恢復操作跪但,需要將一組操作組合在一起,即支持宏命令峦萎。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屡久,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子爱榔,更是在濱河造成了極大的恐慌被环,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搓蚪,死亡現(xiàn)場離奇詭異蛤售,居然都是意外死亡,警方通過查閱死者的電腦和手機妒潭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揣钦,“玉大人雳灾,你說我怎么就攤上這事》氚迹” “怎么了谎亩?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宇姚。 經(jīng)常有香客問我匈庭,道長,這世上最難降的妖魔是什么浑劳? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任阱持,我火速辦了婚禮,結(jié)果婚禮上魔熏,老公的妹妹穿的比我還像新娘衷咽。我一直安慰自己鸽扁,他們只是感情好,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布镶骗。 她就那樣靜靜地躺著桶现,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鼎姊。 梳的紋絲不亂的頭發(fā)上骡和,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機與錄音相寇,去河邊找鬼即横。 笑死,一個胖子當著我的面吹牛裆赵,可吹牛的內(nèi)容都是我干的东囚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼战授,長吁一口氣:“原來是場噩夢啊……” “哼页藻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起植兰,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤份帐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后楣导,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體废境,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年筒繁,在試婚紗的時候發(fā)現(xiàn)自己被綠了噩凹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡毡咏,死狀恐怖驮宴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呕缭,我是刑警寧澤堵泽,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站恢总,受9級特大地震影響迎罗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜片仿,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一纹安、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦钻蔑、人聲如沸啥刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽可帽。三九已至,卻和暖如春窗怒,著一層夾襖步出監(jiān)牢的瞬間映跟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工扬虚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留努隙,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓辜昵,卻偏偏與公主長得像荸镊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堪置,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

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

  • 1 場景問題# 1.1 如何開機## 估計有些朋友看到這個標題會非常奇怪躬存,電腦裝配好了,如何開機舀锨?不就是按下啟動按...
    七寸知架構(gòu)閱讀 2,828評論 1 59
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理岭洲,服務(wù)發(fā)現(xiàn),斷路器坎匿,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 設(shè)計模式匯總 一盾剩、基礎(chǔ)知識 1. 設(shè)計模式概述 定義:設(shè)計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,939評論 1 15
  • 目錄 本文的結(jié)構(gòu)如下: 什么是命令模式 為什么要用該模式 模式的結(jié)構(gòu) 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應(yīng)用 總...
    w1992wishes閱讀 1,112評論 2 9
  • 1.最近做的一個項目用到了流水布局,簡單粗暴,找了個demo放進去.剛開始靜態(tài)頁面感覺還不錯. demo: htt...
    Zavier_copy閱讀 1,060評論 0 0