命令模式

簡介

Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.
將一個請求封裝成一個對象芦昔,從而讓你使用不同的請求把客戶端參數(shù)化天通,對請求排隊或者記錄請求日志雄卷,可以提供命令的撤銷和恢復(fù)功能。

命令模式(Command Pattern)是對命令的封裝郭蕉,每一個命令都是一個操作:請求的一方發(fā)出請求要求執(zhí)行一個操作疼邀;接收的一方收到請求,并執(zhí)行操作召锈。命令模式 解耦了請求方和接收方旁振,請求方只需請求執(zhí)行命令,不用關(guān)心命令是怎樣被接收,怎樣被操作以及是否被執(zhí)行····

軟件系統(tǒng)中规求,行為請求者與行為實現(xiàn)者通常是一種緊耦合關(guān)系筐付,因為這樣的實現(xiàn)簡單明了卵惦。但緊耦合關(guān)系缺乏擴展性阻肿,在某些場合中,當(dāng)需要為行為進行記錄沮尿,撤銷或重做等處理時丛塌,只能修改源碼。而 命令模式 通過為請求與實現(xiàn)間引入一個抽象命令接口畜疾,解耦了請求與實現(xiàn)赴邻,并且中間件是抽象的,它可以有不同的子類實現(xiàn)啡捶,因此其具備擴展性姥敛。

命令模式 本質(zhì):解耦命令請求與處理

主要解決

當(dāng)系統(tǒng)的某項操作具備命令語義時,且命令實現(xiàn)不穩(wěn)定(變化)瞎暑,那么可以通過 命令模式 解耦請求與實現(xiàn)彤敛,利用抽象命令接口使請求方代碼架構(gòu)穩(wěn)定,封裝接收方具體命令實現(xiàn)細(xì)節(jié)了赌。接收方與抽象命令接口呈現(xiàn)弱耦合(內(nèi)部方法無需一致)墨榄,具備良好的擴展性。

優(yōu)缺點

優(yōu)點

  • 通過引入中間件(抽象接口)勿她,解耦了命令請求與實現(xiàn)袄秩;
  • 擴展性良好,可以很容易地增加新命令逢并;
  • 支持組合命令之剧,支持命令隊列;
  • 可以在現(xiàn)有命令的基礎(chǔ)上砍聊,增加額外功能(比如日志記錄···背稼,結(jié)合 裝飾器模式 更酸爽);

缺點

  • 具體命令類可能過多辩恼;
  • 命令模式 的結(jié)果其實就是接收方的執(zhí)行結(jié)果雇庙,但是為了以命令的形式進行架構(gòu),解耦請求與實現(xiàn)灶伊,引入了額外類型結(jié)構(gòu)(引入了請求方與抽象命令接口)疆前,增加了理解上的困難(不過這也是設(shè)計模式帶來的一個通病,抽象必然會引入額外類型聘萨;抽象肯定比緊密難理解)竹椒;

使用場景

  • 現(xiàn)實語義中具備 ”命令“ 的操作(如命令菜單,shell命令···)米辐;
  • 需要將請求與實現(xiàn)解耦胸完;
  • 需要支持命令的撤銷(Undo)操作和恢復(fù)(Redo)操作书释;
  • 需要支持命令組合操作(宏命令);

模式講解

首先看下 命令模式 的通用 UML 類圖:

命令模式

從 UML 類圖中赊窥,我們可以看到爆惧,命令模式 主要包含四種角色:

  • 接收者角色(Receiver):該類負(fù)責(zé)具體實施或執(zhí)行一個請求;
  • 命令角色(Command):定義需要執(zhí)行的所有命令行為锨能;
  • 具體命令角色(ConcreteCommand):該類內(nèi)部維護一個 接收者(Receiver)扯再,在其execut方法中調(diào)用 Receiver 的相關(guān)方法;
  • 請求者角色(Invoker):接收客戶端的命令址遇,并執(zhí)行命令熄阻;

:從 命令模式 的 UML 類圖中,其實可以很清晰地看出:Command的出現(xiàn)就是作為ReceiverInvoker的中間件倔约,解耦了彼此秃殉。而之所以引入Command中間件,我覺得是以下兩方面原因:

  • 解耦請求與實現(xiàn):即解耦了InvokerReceiver浸剩,因為在 UML 類圖中钾军,Invoker是一個具體的實現(xiàn),等待接收客戶端傳入命令(即Invoker與客戶端耦合)乒省,Invoker處于業(yè)務(wù)邏輯區(qū)域巧颈,應(yīng)當(dāng)是一個穩(wěn)定的結(jié)構(gòu)。而Receiver是屬于業(yè)務(wù)功能模塊袖扛,是經(jīng)常變動的砸泛;如果沒有Command,則Invoker緊耦合Receiver蛆封,一個穩(wěn)定的結(jié)構(gòu)依賴了一個不穩(wěn)定的結(jié)構(gòu)唇礁,就會導(dǎo)致整個結(jié)構(gòu)都不穩(wěn)定了。這也就是Command引入的原因:不僅僅是解耦請求與實現(xiàn)惨篱,同時穩(wěn)定(Invoker)依賴穩(wěn)定(Command)盏筐,結(jié)構(gòu)還是穩(wěn)定的;
  • 擴展性增強:擴展性體現(xiàn)在兩個方面:1. Receiver屬于底層細(xì)節(jié)砸讳,可以通過更換不同的Receiver達到不同的細(xì)節(jié)實現(xiàn)琢融;2. Command接口本身就是抽象的,本身就具備擴展性簿寂;而且由于命令對象本身就具備抽象漾抬,如果結(jié)合 裝飾器模式,功能擴展簡直如魚得水常遂。

以下是 命令模式 的通用代碼:

class Client {
    public static void main(String[] args) {
        ICommand cmd = new ConcreteCommand();
        Invoker invoker = new Invoker(cmd);
        invoker.action();
    }

    //接收者
    static class Receiver {
        public void action() {
            System.out.println("執(zhí)行具體操作");
        }
    }

    //抽象命令接口
    interface ICommand {
        void execute();
    }

    //具體命令
    static class ConcreteCommand implements ICommand {
        // 直接創(chuàng)建接收者纳令,不暴露給客戶端
        private Receiver mReceiver = new Receiver();

        @Override
        public void execute() {
            this.mReceiver.action();
        }
    }

    //請求者
    static class Invoker {
        private ICommand mCmd;

        private Invoker(ICommand cmd) {
            this.mCmd = cmd;
        }

        public void action() {
            this.mCmd.execute();
        }
    }
}

:在一個系統(tǒng)中,不同的命令對應(yīng)不同的請求,也就是說無法把請求抽象化平绩,因此 命令模式 中的Receiver是具體實現(xiàn)圈匆;但是如果在某一個模塊中,可以對Receiver進行抽象捏雌,其實這就變相使用到了 橋接模式Command類具備兩個變化的維度:CommandReceiver)跃赚,這樣子的擴展性會更加優(yōu)秀。

舉個例子

例子:假如現(xiàn)有我們有一個遙控器腹忽,可以控制風(fēng)扇的風(fēng)力大小来累,分為大砚作,中窘奏,小,關(guān)閉四個程度葫录,請使用程序進行實現(xiàn)着裹。

分析:上面的例子涉及兩個物體:遙控器和風(fēng)扇,直接的思路就是遙控器緊耦合風(fēng)扇米同,然后遙控器內(nèi)部暴露控制風(fēng)扇風(fēng)力等級接口骇扇。但是遙控器后續(xù)可能還可以對其他電器設(shè)備(如空調(diào)等)進行控制,因此有必要解耦遙控器和具體電器設(shè)備的緊密聯(lián)系面粮。對遙控器進行操作少孝,相當(dāng)于發(fā)出一個指令(命令),讓對應(yīng)的電器設(shè)備進行工作熬苍,那么 命令模式 是非常切合這個場景的稍走。

具體代碼如下:

class Client {
    public static void main(String[] args) {
        RemoteController remote = new RemoteController();
        Fan fan = new Fan();

        ICommand cmd = new TurnMinCommand(fan);
        remote.action(cmd);
        
        cmd = new TurnMidCommand(fan);
        remote.action(cmd);

        cmd = new TurnMaxCommand(fan);
        remote.action(cmd);
        
        cmd = new TurnOffCommand(fan);
        remote.action(cmd);
    }

    //Receiver
    static class Fan {
        public void turnMin() {
            System.out.println("Fan in Min degree");
        }

        public void turnMid() {
            System.out.println("Fan in Mid degree");
        }

        public void turnMax() {
            System.out.println("Fan in Max degree");
        }

        public void turnOff() {
            System.out.println("Fan off");
        }
    }

    //Command
    interface ICommand {
        void execute();
    }

    //ComcreteCommand
    static class TurnMinCommand implements ICommand {
        private Fan mFan;

        public TurnMinCommand(Fan fan) {
            this.mFan = fan;
        }

        @Override
        public void execute() {
            this.mFan.turnMin();
        }
    }

    //ComcreteCommand
    static class TurnMidCommand implements ICommand {
        private Fan mFan;

        public TurnMidCommand(Fan fan) {
            this.mFan = fan;
        }

        @Override
        public void execute() {
            this.mFan.turnMid();
        }
    }

    //ComcreteCommand
    static class TurnMaxCommand implements ICommand {
        private Fan mFan;

        public TurnMaxCommand(Fan fan) {
            this.mFan = fan;
        }

        @Override
        public void execute() {
            this.mFan.turnMax();
        }
    }
    //ComcreteCommand
    static class TurnOffCommand implements ICommand {
        private Fan mFan;

        public TurnOffCommand(Fan fan) {
            this.mFan = fan;
        }

        @Override
        public void execute() {
            this.mFan.turnOff();
        }
    }

    //Invoker
    static class RemoteController {
        public void action(ICommand cmd) {
            cmd.execute();
        }
    }
}

由于遙控器已經(jīng)與具體電器解耦了,以后如果想擴展新命令柴底,只需增加即可婿脸,遙控器結(jié)構(gòu)無需改動。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柄驻,一起剝皮案震驚了整個濱河市狐树,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸿脓,老刑警劉巖抑钟,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異野哭,居然都是意外死亡在塔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門虐拓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來心俗,“玉大人,你說我怎么就攤上這事〕情唬” “怎么了揪利?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狠持。 經(jīng)常有香客問我疟位,道長,這世上最難降的妖魔是什么喘垂? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任甜刻,我火速辦了婚禮,結(jié)果婚禮上正勒,老公的妹妹穿的比我還像新娘得院。我一直安慰自己,他們只是感情好章贞,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布祥绞。 她就那樣靜靜地躺著,像睡著了一般鸭限。 火紅的嫁衣襯著肌膚如雪蜕径。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天败京,我揣著相機與錄音兜喻,去河邊找鬼。 笑死赡麦,一個胖子當(dāng)著我的面吹牛朴皆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隧甚,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼车荔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戚扳?” 一聲冷哼從身側(cè)響起忧便,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帽借,沒想到半個月后珠增,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡砍艾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年蒂教,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脆荷。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡凝垛,死狀恐怖懊悯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梦皮,我是刑警寧澤炭分,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站剑肯,受9級特大地震影響捧毛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜让网,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一呀忧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溃睹,春花似錦而账、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惜犀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狠裹,已是汗流浹背虽界。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涛菠,地道東北人莉御。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像俗冻,于是被迫代替她去往敵國和親礁叔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

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

  • 【學(xué)習(xí)難度:★★★☆☆迄薄,使用頻率:★★★★☆】直接出處:命令模式梳理和學(xué)習(xí):https://github.com/...
    BruceOuyang閱讀 827評論 0 3
  • 1 場景問題# 1.1 如何開機## 估計有些朋友看到這個標(biāo)題會非常奇怪琅关,電腦裝配好了,如何開機讥蔽?不就是按下啟動按...
    七寸知架構(gòu)閱讀 2,820評論 1 59
  • 目的: 任何模式的出現(xiàn)涣易,都是為了解決一些特定的場景的耦合問題,以達到對修改封閉冶伞,對擴展開放的效果新症。命令模式也不例外...
    時待吾閱讀 310評論 0 0
  • 目錄 本文的結(jié)構(gòu)如下: 什么是命令模式 為什么要用該模式 模式的結(jié)構(gòu) 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應(yīng)用 總...
    w1992wishes閱讀 1,102評論 2 9
  • 我多么希望,有一個門口 早晨响禽,陽光照在草上 我們站著徒爹,扶著自己的門窗 門很低荚醒,但太陽是明亮的 草在結(jié)它的種子 風(fēng)在...
    似水年華兔閱讀 297評論 0 2