命令模式

命令模式的本質(zhì)是對(duì)命令進(jìn)行封裝,將發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開扎运。關(guān)鍵在于引入了抽象命令接口沼侣,且發(fā)送者針對(duì)抽象命令接口編程,只有實(shí)現(xiàn)了抽象命令接口的具體命令才能與接收者相關(guān)聯(lián)赁项。

  • UML:


    image.png
  • 模型:家電自動(dòng)遙控器
  • 特點(diǎn):我們想用一個(gè)遙控器來(lái)控制多個(gè)家電,例如澈段,電燈悠菜,音響,空調(diào)败富,電視等悔醋。家電是請(qǐng)求的接收者,遙控器是請(qǐng)求的發(fā)送者兽叮,遙控器上有一些按鈕芬骄,不同的按鈕對(duì)應(yīng)家電的不同操作。抽象命令角色由一個(gè)命令接口來(lái)扮演鹦聪,有三個(gè)具體的命令類實(shí)現(xiàn)了抽象命令接口账阻,這三個(gè)具體命令類分別代表三種操作:打開家電、關(guān)閉關(guān)閉和啥都不干泽本。
public class Light {
        //燈的位置淘太,臥室,廚房...
    String loc = "";
    public Light(String loc) {
        this.loc = loc;
    }
    public void On() {
        System.out.println(loc + " On");
    }
    public void Off() {
        System.out.println(loc + " Off");
    }
}
//音響設(shè)備
public class Stereo {
    static int volume = 0;
    public void On() {
        System.out.println("Stereo On");
    }
    public void Off() {
        System.out.println("Stereo Off");
    }
    public void SetCd() {
        System.out.println("Stereo SetCd");
    }
    public void SetVol(int vol) {
        volume = vol;
        System.out.println("Stereo volume=" + volume);
    }
    public int GetVol() {
        return volume;
    }
    public void Start() {
        System.out.println("Stereo Start");
    }
}
//模擬設(shè)備廠家提供的遙控器控制接口
public interface Control {
    public void onButton(int slot);
    public void offButton(int slot);
    public void undoButton();
}

使用傳統(tǒng)的面向?qū)ο蟮脑O(shè)計(jì)规丽,一般是創(chuàng)建一個(gè)遙控器實(shí)例蒲牧,將燈、音響的實(shí)例傳給遙控器嘁捷,然后根據(jù)遙控器的按鈕設(shè)置不同的家電操作造成,例如按下1號(hào)鍵是音響開显熏,2號(hào)鍵是音響加音量等

public class TraditionControl implements Control {
    Light light;
    Stereo stereo;
    public TraditionControl(Light light, Stereo stereo) {
        this.light = light;
        this.stereo = stereo;
    }
    @Override
    public void onButton(int slot) {
        // TODO Auto-generated method stub
        switch (slot) {
        case 0:
            light.On();
            break;
        case 1:
            stereo.On();
            break;
        case 2:
            int vol = stereo.GetVol();
            if (vol < 11) {
                stereo.SetVol(++vol);
            }
            break;
        }
    }
    @Override
    public void offButton(int slot) {
        // TODO Auto-generated method stub
        switch (slot) {
        case 0:
            light.Off();
            break;
        case 1:
            stereo.Off();
            break;
        case 2:
            int vol = stereo.GetVol();
            if (vol > 0) {
                stereo.SetVol(--vol);
            }
            break;
        }
    }
    @Override
    public void undoButton() {  
    }
}

這樣設(shè)計(jì)雄嚣,如果要加一種設(shè)備,這種設(shè)計(jì)的控制器就不好添加了,首先要在控制器中注入設(shè)備缓升,按鍵處理中要再加新設(shè)備的處理鼓鲁。所以,這樣就高耦合了港谊,不利于擴(kuò)展骇吭。

  • 使用命令模式:
//將命令抽象處理請(qǐng)求接口
public interface Command {
    public void execute();
    public void undo();
}
//臥室關(guān)燈命令
public class LightOffCommand implements Command {
    private Light light;
    public LightOffCommand(Light light){
        this.light=light;
    }
    @Override
    public void execute() {
        light.Off();
    }
    @Override
    public void undo() {
        light.On();
    }
}
//臥室開燈命令
public class LightOnCommand implements Command {
    private Light light;
    public LightOnCommand(Light light){
        this.light=light;
    }
    @Override
    public void execute() {
        light.On();
    }
    @Override
    public void undo() {
        light.Off();
    }
}
//操作無(wú)響應(yīng)命令
public class NoCommand implements Command {
    @Override
    public void execute() {}
    @Override
    public void undo() {}
}
//音響開命令
public class StereoOnCommand implements Command {
    private Stereo setreo;
    public StereoOnCommand(Stereo setreo){
        this.setreo=setreo;
    }
    @Override
    public void execute() {
        setreo.On();
        setreo.SetCd();
    }
    @Override
    public void undo() {
        setreo.Off();
    }
}
//音響關(guān)命令
public class StereoOffCommand implements Command {
    private Stereo setreo;
    public StereoOffCommand(Stereo setreo){
        this.setreo=setreo;
    }
    @Override
    public void execute() {
        setreo.Off();
    }
    @Override
    public void undo() {
        setreo.On();
        setreo.SetCd();
    }
}
//音響加音量
public class StereoAddVolCommand implements Command{
    private Stereo setreo;
    public StereoAddVolCommand(Stereo setreo){
        this.setreo=setreo;
    }
    @Override
    public void execute() {
    int vol=    setreo.GetVol();
    if(vol<11){
        setreo.SetVol(++vol);
    }   
    }
    @Override
    public void undo() {
    int vol=    setreo.GetVol();
    if(vol>0){
        setreo.SetVol(--vol);
    }   
    }
}
//音響減音量命令
public class StereoSubVolCommand implements Command{
    private Stereo setreo;
    public StereoSubVolCommand(Stereo setreo){
        this.setreo=setreo;
    }
    @Override
    public void execute() {
    int vol=    setreo.GetVol();
    if(vol>0){
        setreo.SetVol(--vol);
    }   
    }
    @Override
    public void undo() {
    int vol=    setreo.GetVol();
    if(vol<11){
        setreo.SetVol(++vol);
    }   
    }
}

//當(dāng)有了這樣命令之后,我們只需要將命令設(shè)置給遙控器的不同的按鍵即可歧寺,用戶在按鍵時(shí)會(huì)請(qǐng)求執(zhí)行已經(jīng)設(shè)定好的命令燥狰,由命令再調(diào)用具體家電執(zhí)行。

public class CommandModeControl implements Control{
       //所有開命令
    private Command[] onCommands;
      //所有關(guān)命令
    private Command[] offCommands;
       //記錄上一個(gè)執(zhí)行的命令
    private Stack<Command> stack=new Stack<Command>();
    public CommandModeControl(){
        onCommands=new Command[5];
         offCommands=new Command[5];
         Command noCommand=new NoCommand();
                 // 初始化為無(wú)操作命令
         for(int i=0,len=onCommands.length;i<len;i++) {
             onCommands[i]=noCommand;
             offCommands[i]=noCommand;
         } 
    }
       //設(shè)置具體按鍵命令
    public void setCommand(int slot,Command onCommand,Command offCommand){
        onCommands[slot]=onCommand;
         offCommands[slot]=offCommand;
    }
    @Override
    public void onButton(int slot) {
                //執(zhí)行命令
        onCommands[slot].execute();
        stack.push(onCommands[slot]);
    }
    @Override
    public void offButton(int slot) {
        offCommands[slot].execute();
        stack.push(offCommands[slot]);
    }
    @Override
    public void undoButton() {
        stack.pop().undo();
    }
}
//使用遙控器
public class ControlTest {
    public static void main(String[] args) {
                //創(chuàng)建遙控器斜筐,也就是UML中的Invoker實(shí)現(xiàn)類
        CommandModeControl control = new CommandModeControl();
        Light bedroomlight = new Light("BedRoom");
        Light kitchlight = new Light("Kitch");
        Stereo stereo = new Stereo();
        //設(shè)置各種命令
        LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight);
        LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight);
        LightOnCommand kitchlighton = new LightOnCommand(kitchlight);
        LightOffCommand kitchlightoff = new LightOffCommand(kitchlight);

         Command[] oncommands={bedroomlighton,kitchlighton};
         Command[] offcommands={bedroomlightoff,kitchlightoff};

        
        StereoOnCommand stereoOn = new StereoOnCommand(stereo);
        StereoOffCommand stereoOff = new StereoOffCommand(stereo);
        StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo);
        StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo);
                //將命令注入到遙控器中
        control.setCommand(0, bedroomlighton, bedroomlightoff);
        control.setCommand(1, kitchlighton, kitchlightoff);
        control.setCommand(2, stereoOn, stereoOff);
        control.setCommand(3, stereoaddvol, stereosubvol);
              //操作遙控器就能執(zhí)行已經(jīng)綁定好的命令控制家電了
        control.onButton(0);
        control.undoButton();
        //control.offButton(0);
        control.onButton(1);
        control.offButton(1);
        control.onButton(2);
        control.onButton(3);
                
        control.offButton(3);
        control.undoButton();
        control.offButton(2);
        control.undoButton();
    }
}

通過上面的設(shè)計(jì)龙致,遙控器不需要注入家電設(shè)備,而是注入各種命令顷链。如果新加一種家電目代,則只需要擴(kuò)展新的命令,并注入到遙控器中即可嗤练。遙控器不關(guān)心家電的實(shí)現(xiàn)榛了,它是調(diào)用命令接口,解耦了遙控器與家電煞抬。但是可能會(huì)導(dǎo)致某些系統(tǒng)有過多的具體命令類霜大。因?yàn)獒槍?duì)每一個(gè)命令都需要設(shè)計(jì)一個(gè)具體命令類,因此某些系統(tǒng)可能需要大量具體命令類此疹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末僧诚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蝗碎,更是在濱河造成了極大的恐慌湖笨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹦骑,死亡現(xiàn)場(chǎng)離奇詭異慈省,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)眠菇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門边败,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捎废,你說我怎么就攤上這事笑窜。” “怎么了登疗?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵排截,是天一觀的道長(zhǎng)嫌蚤。 經(jīng)常有香客問我,道長(zhǎng)断傲,這世上最難降的妖魔是什么脱吱? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮认罩,結(jié)果婚禮上箱蝠,老公的妹妹穿的比我還像新娘。我一直安慰自己垦垂,他們只是感情好宦搬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劫拗,像睡著了一般床三。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杨幼,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天撇簿,我揣著相機(jī)與錄音,去河邊找鬼差购。 笑死四瘫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的欲逃。 我是一名探鬼主播找蜜,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稳析!你這毒婦竟也來(lái)了洗做?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤彰居,失蹤者是張志新(化名)和其女友劉穎诚纸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陈惰,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畦徘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抬闯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井辆。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖溶握,靈堂內(nèi)的尸體忽然破棺而出杯缺,到底是詐尸還是另有隱情,我是刑警寧澤睡榆,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布萍肆,位于F島的核電站廉赔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏匾鸥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一碉纳、第九天 我趴在偏房一處隱蔽的房頂上張望勿负。 院中可真熱鬧,春花似錦劳曹、人聲如沸奴愉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锭硼。三九已至,卻和暖如春蜕劝,著一層夾襖步出監(jiān)牢的瞬間檀头,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工岖沛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暑始,地道東北人谈截。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓摊聋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親男应。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唉俗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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

  • 1 場(chǎng)景問題# 1.1 如何開機(jī)## 估計(jì)有些朋友看到這個(gè)標(biāo)題會(huì)非常奇怪嗤朴,電腦裝配好了,如何開機(jī)虫溜?不就是按下啟動(dòng)按...
    七寸知架構(gòu)閱讀 2,828評(píng)論 1 59
  • 生活場(chǎng)景分析 今天來(lái)學(xué)習(xí)命令模式雹姊,先從一個(gè)生活中的例子入手吧,這樣理解起來(lái)也比較容易衡楞。大家應(yīng)該有用過那種萬(wàn)能遙控器...
    西木柚子閱讀 730評(píng)論 2 6
  • 目錄 本文的結(jié)構(gòu)如下: 什么是命令模式 為什么要用該模式 模式的結(jié)構(gòu) 代碼示例 優(yōu)點(diǎn)和缺點(diǎn) 適用環(huán)境 模式應(yīng)用 總...
    w1992wishes閱讀 1,115評(píng)論 2 9
  • 序不知道是不是因?yàn)榭磿鵂顟B(tài)不好容为,書上的例子確實(shí)總是有種云里霧里的感覺,不過幸好有程序寺酪,看程序基本上就明白這個(gè)所謂的...
    吃根香蕉壓壓驚閱讀 367評(píng)論 0 0
  • 《Head First設(shè)計(jì)模式》讀書筆記 命令模式(封裝調(diào)用) 一坎背,場(chǎng)景介紹 1,需求 設(shè)計(jì)一個(gè)家電自動(dòng)化遙控器的...
    呆麻子閱讀 953評(píng)論 1 11