命令模式

說實(shí)話這個(gè)模式挺令人糾結(jié)的,但從這個(gè)模式的定義上來看渠啊,有點(diǎn)讓人摸不到什么頭腦输吏,而且查看資料以后會(huì)發(fā)現(xiàn)還是有點(diǎn)稀里糊涂的,說懂了吧也很簡(jiǎn)單替蛉,也不懂吧也有不懂的理由贯溅,于是查閱手頭的各種書籍,在此寫下心得體會(huì)躲查,算是加深一下印象它浅。

命令模式UML:

image.png
命令模式的定義:將請(qǐng)求封裝成一個(gè)對(duì)象,從而讓用戶使用不同的請(qǐng)求把客戶端參數(shù)化熙含,以及支持可撤銷和恢復(fù)的功能罚缕。

定義的理解:

請(qǐng)求:

客戶端要求系統(tǒng)執(zhí)行的操作,在java的世界里面就是某個(gè)對(duì)象的方法怎静。

Command:

請(qǐng)求封裝成的對(duì)象,該對(duì)象是命令模式的主角邮弹。也就是說將請(qǐng)求方法封裝成一個(gè)命令對(duì)象黔衡,通過操作命令對(duì)象來操作請(qǐng)求方法。在命令模式是有若干個(gè)請(qǐng)求的腌乡,需要將這些請(qǐng)求封裝成一條條命令對(duì)象盟劫,客戶端只需要調(diào)用不同的命令就可以達(dá)到將請(qǐng)求參數(shù)化的目的。將一條條請(qǐng)求封裝成一條條命定對(duì)象之后与纽,客戶端發(fā)起的就是一個(gè)個(gè)命令對(duì)象了侣签,而不是原來的請(qǐng)求方法!

Receiver:

有命令急迂,當(dāng)然有命令的接收者對(duì)象:如果有只有命令影所,沒有接受者,那不就是光棍司令了僚碎?沒有電視機(jī)或者電腦主機(jī)猴娩,你對(duì)著電視機(jī)遙控器或者電腦鍵盤狂按有毛用?Receiver對(duì)象的主要作用就是受到命令后執(zhí)行對(duì)應(yīng)的操作勺阐。對(duì)于點(diǎn)擊遙控器發(fā)起的命令來說卷中,電視機(jī)就是這個(gè)Receiver對(duì)象,比如按了待機(jī)鍵渊抽,電視機(jī)收到命令后就執(zhí)行了待機(jī)操作蟆豫,進(jìn)入待機(jī)狀態(tài)。

Client:

但是有一個(gè)問題擺在眼前懒闷,命令對(duì)象現(xiàn)在已經(jīng)有了十减,但是誰來負(fù)責(zé)創(chuàng)建命令呢?這里就引出了客戶端Client對(duì)象愤估,再命令模式中命令是有客戶端來創(chuàng)建的嫉称。打個(gè)比方來說,操作遙控器的那個(gè)人灵疮,就是扮演的客戶端的角色。人按下遙控器的不同按鍵壳繁,來創(chuàng)建一條條命令震捣。

Invoker:

現(xiàn)在創(chuàng)建命令的對(duì)象Client也已經(jīng)露臉了,它負(fù)責(zé)創(chuàng)建一條條命令闹炉,那么誰來使用或者調(diào)度這個(gè)命令呢蒿赢?--命令的使用者就是Invoker對(duì)象了,還是拿人渣触,遙控器羡棵,電視機(jī)來做比喻,遙控器就是這個(gè)Invoker對(duì)象嗅钻,遙控器負(fù)責(zé)使用客戶端創(chuàng)建的命令對(duì)象皂冰。該Invoker對(duì)象負(fù)責(zé)要求命令對(duì)象執(zhí)行請(qǐng)求店展,通常會(huì)持有命令對(duì)象,可以持有很多的命令對(duì)象秃流。

上面的講解著實(shí)有些啰嗦赂蕴,下面就用看電視的人(Watcher),電視機(jī)(Television),遙控器(TeleController)來模擬一下這個(gè)命令模式,其中Watcher是Client角色,Television是Receiver角色舶胀,TeleController是Invoker角色概说。

首先設(shè)計(jì)一個(gè)簡(jiǎn)單的電視機(jī)的對(duì)象:

//電視機(jī)對(duì)象:提供了播放不同頻道的方法

public class Television {
    
    public void playCctv1() {
        System.out.println("--CCTV1--");
    }

    public void playCctv2() {
        System.out.println("--CCTV2--");
    }

    public void playCctv3() {
        System.out.println("--CCTV3--");
    }

    public void playCctv4() {
        System.out.println("--CCTV4--");
    }

    public void playCctv5() {
        System.out.println("--CCTV5--");
    }

    public void playCctv6() {
        System.out.println("--CCTV6--");
    }

}

電視機(jī)的類創(chuàng)建好了,本文會(huì)以“非命令模式“和“命令模式“兩種實(shí)現(xiàn)看電視的不同之處嚣伐,加深對(duì)命令模式的理解糖赔。

非命令模式實(shí)現(xiàn):

如果不用命令模式的話,其實(shí)實(shí)現(xiàn)看電視的功能很簡(jiǎn)單轩端,首先設(shè)計(jì)一個(gè)看電視的人的類:Watcher放典;既然要看電視,所以Watcher內(nèi)部需要持有一個(gè)電視機(jī)的引用船万。如此簡(jiǎn)單的Watcher搞定:

//觀看電視的死宅類
public class Watcher {
    //持有一個(gè)
    public Television tv;

    public Watcher(Television tv) {
        this.tv = tv;
    }

    public void playCctv1() {
        tv.playCctv1();
    }

    public void playCctv2() {
        tv.playCctv2();
    }

    public void playCctv3() {
        tv.playCctv3();
    }

    public void playCctv4() {
        tv.playCctv4();
    }

    public void playCctv5() {
        tv.playCctv5();
    }

    public void playCctv6() {
        tv.playCctv6();
    }
}

所以簡(jiǎn)單的調(diào)用就實(shí)現(xiàn)了:

public static void main(String args[]) {
        Watcher watcher = new Watcher(new Television());
        watcher.playCctv1();
        watcher.playCctv2();
        watcher.playCctv3();
        watcher.playCctv4();
        watcher.playCctv5();
        watcher.playCctv6();
    }

執(zhí)行結(jié)果:

--CCTV1--
--CCTV2--
--CCTV3--
--CCTV4--
--CCTV5--
--CCTV6--

可以看出Watcher類和Television完全的耦合在一起了刻撒,目前本文的電視機(jī)對(duì)象只能播放六個(gè)電視臺(tái),如果需要增添全國所有主流衛(wèi)視的話耿导,需要做如下改動(dòng):
1声怔、修改Television對(duì)象,增加若干個(gè)的playXXTV()的方法來播放不同的衛(wèi)視舱呻。
2醋火、修改Watcher,也添加若干個(gè)對(duì)應(yīng)的playXXTV()的方法箱吕,調(diào)用Television的playXXTV()芥驳,如下:

 public void playXXTV() {
        tv.playXXTV();
    }

但是這明顯違背了“對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放“的重要設(shè)計(jì)原則茬高。
別的不說兆旬,就拿本看電視來說,比如調(diào)用playXXTV()的順序是隨即的怎栽,也就是你胡亂切換了一通:比如你沿著cctv1丽猬、cctv2、cctv3熏瞄、cctv4脚祟、xxtv、yytv…nntv的順序來看電視强饮,也就是發(fā)生了如下調(diào)用:

        watcher.playCctv1();
        watcher.playCctv2();
        watcher.playCctv3();
        watcher.playCctv4();
        watcher.playCctv5();
        watcher.playCctv6();
        watcher.playXXtv();
        watcher.playYYtv();
        watcher.playNNtv();

由桌,當(dāng)前你在看nntv,如果你想看上一個(gè)看過的臺(tái)也就是yytv,這很簡(jiǎn)單,只要在playNNtv() 后面,調(diào)用watcher.playYYtv();即可行您,但是如果你想要一直回退到cctv1呢铭乾?那么你就話發(fā)生如下可怕的調(diào)用:

        watcher.playCctv1();
        watcher.playCctv2();
        watcher.playCctv3();
        watcher.playCctv4();
        watcher.playCctv5();
        watcher.playCctv6();
        watcher.playXXtv();
        watcher.playYYtv();
        watcher.playNNtv();

        
        watcher.playYYtv();
        watcher.playXXtv();
        watcher.playCctv6();
        watcher.playCctv5();
        watcher.playCctv4();
        watcher.playCctv3();
        watcher.playCctv2();
        watcher.playCctv1();

為什么會(huì)這樣呢?因?yàn)閷?duì)于之前播放的哪個(gè)衛(wèi)視并沒有記錄功能邑雅。是時(shí)候讓命令模式來出來解決 問題了片橡,通過命令模式的實(shí)現(xiàn),對(duì)比下就能體會(huì)到命令模式的巧妙之處淮野。

命令模式的實(shí)現(xiàn):

1捧书、設(shè)計(jì)一個(gè)抽象的命令類:
在本系統(tǒng)中,命令的接收者對(duì)象就是電視機(jī)Tevevision了:

public abstract class Command {
    //命令接收者:電視機(jī)
    protected Television television;

    public Command(Television television) {
        this.television = television;
    }

    //命令執(zhí)行
    abstract void execute();
}

將播放各個(gè)衛(wèi)視的操作封裝成一個(gè)一個(gè)命令骤星,實(shí)現(xiàn)如下:

//播放cctv1的命令
public class CCTV1Command extends Command {
    @Override
    void execute() {
        television.playCctv1();
    }
}

//播放cctv2的命令
public class CCTV6Command extends Command {
    @Override
    void execute() {
        television.playCctv2();
    }
}
经瓷。。洞难。舆吮。。队贱。色冀。。

//播放cctv6的命令
public class CCTV1Command extends Command {
    @Override
    void execute() {
        television.playCctv6();
    }
}

抽象類Command的幾個(gè)子類實(shí)現(xiàn)也很簡(jiǎn)單柱嫌,就是將電視機(jī)TeleVision對(duì)象的playXxTV方法分布于不同的命令對(duì)象中锋恬,且因?yàn)椴煌拿顚?duì)象擁有共同的抽象類,我們很容易將這些名利功能放入一個(gè)數(shù)據(jù)結(jié)構(gòu)(比如數(shù)組)中來存儲(chǔ)執(zhí)行過的命令编丘。

命令對(duì)象設(shè)計(jì)好了与学,那么就引入命令的調(diào)用著Invoker對(duì)象了,在此例子中電視遙控器TeleController就是扮演的這個(gè)角色:

public class TeleController {
    //播放記錄
    List<Command> historyCommand = new ArrayList<Command>();

    //切換衛(wèi)視
    public void switchCommand(Command command) {
        historyCommand.add(command);
        command.execute();
    }

    //遙控器返回命令
    public void back() {
        if (historyCommand.isEmpty()) {
            return;
        }
        int size = historyCommand.size();
        int preIndex = size-2<=0?0:size-2;

        //獲取上一個(gè)播放某衛(wèi)視的命令
        Command preCommand = historyCommand.remove(preIndex);

        preCommand.execute();
    }

}

很簡(jiǎn)答嘉抓,遙控器對(duì)象持有一個(gè)命令集合索守,用來記錄已經(jīng)執(zhí)行過的命令。新的命令對(duì)像作為switchCommand參數(shù)來添加到集合中抑片,注意在這里就體現(xiàn)出了讓上文所術(shù)的請(qǐng)求參數(shù)化的目的卵佛。且遙控器類也提供了back方法用來模擬真實(shí)遙控器的返回功能:
所以main方法的實(shí)現(xiàn)如下:

       //創(chuàng)建一個(gè)電視機(jī)
        Television tv = new Television();
        //創(chuàng)建一個(gè)遙控器
        TeleController teleController = new TeleController();

        teleController.switchCommand(new CCTV1Command(tv));
        teleController.switchCommand(new CCTV2Command(tv));
        teleController.switchCommand(new CCTV4Command(tv));
        teleController.switchCommand(new CCTV3Command(tv));
        teleController.switchCommand(new CCTV5Command(tv));
        teleController.switchCommand(new CCTV1Command(tv));
        teleController.switchCommand(new CCTV6Command(tv));
        System.out.println("------返回上一個(gè)衛(wèi)視--------");
        //模擬遙控器返回鍵
        teleController.back();
        teleController.back();

執(zhí)行結(jié)果如下:

--CCTV1--
--CCTV2--
--CCTV4--
--CCTV3--
--CCTV5--
--CCTV1--
--CCTV6--
----------返回上一個(gè)衛(wèi)視-------------
--CCTV1--
--CCTV5--

從上面的例子我們可以看出,命令模式的主要特點(diǎn)就是將請(qǐng)求封裝成一個(gè)個(gè)命令敞斋,以命令為參數(shù)來進(jìn)行切換级遭,達(dá)到請(qǐng)求參數(shù)化的目的,且還能通過集合這個(gè)數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)已經(jīng)執(zhí)行的請(qǐng)求渺尘,進(jìn)行回退操作。而且如果需要添加新的電視頻道说敏,只需要添加新的命令類即可

而非命令模式中鸥跟,看電視的人和電視耦合在一起;而新的命令模式則使用一個(gè)遙控器就將人和電視機(jī)解耦∫阶桑總讓你抱著電視機(jī)你也不樂意不是枫匾?

結(jié)合上述例子,最后用一個(gè)圖來簡(jiǎn)單的表示命令模式拟淮。博主喜歡“斗圖".

好了干茉,我對(duì)命令模式的理解到這里就結(jié)束了,如果大家發(fā)現(xiàn)有什么錯(cuò)誤的地方很泊,希望能不吝指正角虫。至于命令模式的缺點(diǎn),如果理解了該模式委造,他的缺點(diǎn)不是顯而易見的嗎戳鹅?博主偷個(gè)懶就不再贅述。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昏兆,一起剝皮案震驚了整個(gè)濱河市枫虏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爬虱,老刑警劉巖隶债,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異跑筝,居然都是意外死亡死讹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門继蜡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來回俐,“玉大人,你說我怎么就攤上這事稀并〗銎模” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵碘举,是天一觀的道長(zhǎng)忘瓦。 經(jīng)常有香客問我,道長(zhǎng)引颈,這世上最難降的妖魔是什么耕皮? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蝙场,結(jié)果婚禮上凌停,老公的妹妹穿的比我還像新娘。我一直安慰自己售滤,他們只是感情好罚拟,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布台诗。 她就那樣靜靜地躺著,像睡著了一般赐俗。 火紅的嫁衣襯著肌膚如雪拉队。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天阻逮,我揣著相機(jī)與錄音粱快,去河邊找鬼。 笑死叔扼,一個(gè)胖子當(dāng)著我的面吹牛事哭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播币励,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼慷蠕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了食呻?” 一聲冷哼從身側(cè)響起流炕,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仅胞,沒想到半個(gè)月后每辟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡干旧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年渠欺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椎眯。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挠将,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出编整,到底是詐尸還是另有隱情舔稀,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布掌测,位于F島的核電站内贮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏汞斧。R本人自食惡果不足惜夜郁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粘勒。 院中可真熱鬧竞端,春花似錦、人聲如沸庙睡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赵颅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暂刘,已是汗流浹背饺谬。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谣拣,地道東北人募寨。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像森缠,于是被迫代替她去往敵國和親拔鹰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • 說實(shí)話這個(gè)模式挺令人糾結(jié)的贵涵,但從這個(gè)模式的定義上來看列肢,有點(diǎn)讓人摸不到什么頭腦,而且查看資料以后會(huì)發(fā)現(xiàn)還是有點(diǎn)稀里糊...
    夜色流冰閱讀 220評(píng)論 0 0
  • 命令模式是什么呢宾茂?考慮一下電視遙控器的場(chǎng)景瓷马,我們按下遙控器的某個(gè)按鍵,電視就會(huì)有相應(yīng)的反應(yīng)(例如換臺(tái))跨晴。按下遙控器...
    yeonon閱讀 228評(píng)論 0 0
  • [TOC] 一欧聘、模式定義 命令模式(Command Pattern):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,使發(fā)出請(qǐng)求的責(zé)任和...
    smileNicky閱讀 377評(píng)論 1 0
  • #1.模板模式 一.模式定義 在模板模式(Template Pattern)中,一個(gè)抽象類公開定義了執(zhí)行它的方法的...
    Sznag閱讀 996評(píng)論 0 1
  • 一 定義 命令模式也屬于行為型設(shè)計(jì)模式之一焕妙。定義:將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象蒋伦,從而讓用戶使用不同的請(qǐng)求把客戶端參數(shù)化...
    Jackson杰閱讀 1,391評(píng)論 0 0