1. 程式人生 > >設計模式之命令模式(二)

設計模式之命令模式(二)

上一次留給大家去做的實踐,不知道大家執行的怎麼樣了呢。

我們通過一個簡單的練習,完成了一個控制開關。那現在,我們打算將遙控器的每個插槽,對應到一個命令這樣就要遙控器變成“呼叫者”。當按下按鈕,相應命令物件的execute()方法就會被呼叫,其結果就是,接收者(例如電燈、風扇、音響)的動作被呼叫。

實現遙控器

public class RemoteControl {
    Command[] onCommands;
    Command[] offCommands;
 

    public RemoteControl() {
        onCommands = new Command[7];
        offCommands = new Command[7];
// 在構造器中,只需例項化並初始化這兩個開與關的陣列
        Command noCommand = new NoCommand();
        for (int i = 0; i < 7; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }

// 這個方法有三個引數,分別是插槽的位置、開的命令、關的命令。這些命令將記錄開關陣列中對應的插槽位置,以供稍後使用
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
 
// 當按下開或關的按鈕,硬體就會負責呼叫對應的方法,也就是onButtonWasPushed或offButtonWasPushed
    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
    }
 
    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
    }
  
    public String toString() {
        StringBuffer stringBuff = new StringBuffer();
        stringBuff.append("\n------ Remote Control -------\n");
        for (int i = 0; i < onCommands.length; i++) {
            stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()
                + "    " + offCommands[i].getClass().getName() + "\n");
        }
        return stringBuff.toString();
    }
}

實現命令

此前我們已經動手實現過LightOnCommand,純粹就是簡單的開和關命令。那現在,我們來為音響編寫開與關的命令。

音響的關閉是毫無難度,就是開啟的時候有點複雜,你知道為什麼嗎?難道音響開了就好了?是否還需要後續其他的動作才能讓音響響起來了?哎呀,小編多嘴了好像。

public class StereoOnWithCDCommand implements Command {
    Stereo stereo;
 
    public StereoOnWithCDCommand(Stereo stereo) {
        this.stereo = stereo;
    }
 
// 開啟音響,需要三個步驟,開啟音響,設定CD播放,設定音量,不然就成啞巴了
    public void execute() {
        stereo.on();
        stereo.setCD();
        stereo.setVolume(11);
    }
}

這裡列舉了一個電燈,一個音響,差不多就把其他類似的都已經搞定了,比如電扇、門,對吧。所以,趕緊看看你之前動手的操作,是不是和小編的差不多。

讓我們繼續看下,多個的是怎麼實現的呢。

public class RemoteLoader {
 
    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();
// 將所有的裝置建立在合適的位置
        Light livingRoomLight = new Light("Living Room");
        Light kitchenLight = new Light("Kitchen");
        CeilingFan ceilingFan= new CeilingFan("Living Room");
        GarageDoor garageDoor = new GarageDoor("");
        Stereo stereo = new Stereo("Living Room");
  
// 建立所有的電燈命令物件
        LightOnCommand livingRoomLightOn = 
                new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = 
                new LightOffCommand(livingRoomLight);
        LightOnCommand kitchenLightOn = 
                new LightOnCommand(kitchenLight);
        LightOffCommand kitchenLightOff = 
                new LightOffCommand(kitchenLight);
  
// 建立吊扇的開與關命令
        CeilingFanOnCommand ceilingFanOn = 
                new CeilingFanOnCommand(ceilingFan);
        CeilingFanOffCommand ceilingFanOff = 
                new CeilingFanOffCommand(ceilingFan);
 
// 建立車庫門的上與下命令
        GarageDoorUpCommand garageDoorUp =
                new GarageDoorUpCommand(garageDoor);
        GarageDoorDownCommand garageDoorDown =
                new GarageDoorDownCommand(garageDoor);
                
// 建立音響的開與關命令
        StereoOnWithCDCommand stereoOnWithCD =
                new StereoOnWithCDCommand(stereo);
        StereoOffCommand  stereoOff =
                new StereoOffCommand(stereo);
 
// 現在已經有了全部的命令,我們將它們載入到遙控器插槽中
        remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
        remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
        remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
        remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
  
        System.out.println(remoteControl);
 
// 在這裡逐步按下每個插槽的開與關按鈕
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(1);
        remoteControl.offButtonWasPushed(1);
        remoteControl.onButtonWasPushed(2);
        remoteControl.offButtonWasPushed(2);
        remoteControl.onButtonWasPushed(3);
        remoteControl.offButtonWasPushed(3);
    }
}

寫文件的時候到了

我們這個主要的設計目標就是讓遙控器程式碼儘可能地簡單,這樣一來,新的廠商類一旦出現,遙控器並不需要隨之修改。因為,我們才用了命令模式,從邏輯上將遙控器的類和廠商的類解耦。我們相信這將降低遙控器的生產成本,並大大地減少維護時所需的費用。

下面的類圖提供了設計的全貌:

撤銷哪去了?

別急別急,小編說的功能都會有的。撤銷功能使用起來就是這樣的:比如說客廳的電燈是關閉的,然後你按下遙控器上的開啟按鈕,自然電燈就被打開了。現在如果按下撤銷按鈕,那麼上一個動作將被倒轉,在這個例子裡,電燈將被關閉。

同樣,我們先來一個簡單的撤銷示例。之前我們用的是execute()方法實現開啟或者關閉的呼叫,那麼我們用undo()方法來執行撤銷操作。即在Command接口裡實現一個同execute()相反的方法undo(),然後在實現類裡將undo()的動作做成和execute()相反的操作即可。

講的有點籠統?在這裡小編就不提供具體的程式碼了,詳細的請看GitHub我的分享吧。

使用狀態實現撤銷

因為電燈這個開關已經撤銷,是很簡單的入門,小編沒有提供原始碼在文中,但是因為還有電風扇這個存在,小編還不得不繼續搞一個高大上的方式。電扇不僅僅是開關,還有檔位的存在,對吧,是不是瞬間有思路了呢?

public class CeilingFan {
    public static final int HIGH = 3;
    public static final int MEDIUM = 2;
    public static final int LOW = 1;
    public static final int OFF = 0;
    String location;
    int speed;
 
    public CeilingFan(String location) {
        this.location = location;
        speed = OFF;
    }
  
    public void high() {
        speed = HIGH;
        System.out.println(location + " ceiling fan is on high");
    } 
 
    public void medium() {
        speed = MEDIUM;
        System.out.println(location + " ceiling fan is on medium");
    }
 
    public void low() {
        speed = LOW;
        System.out.println(location + " ceiling fan is on low");
    }
  
    public void off() {
        speed = OFF;
        System.out.println(location + " ceiling fan is off");
    }
  
    public int getSpeed() {
        return speed;
    }
}

現在我們就來實現風扇的撤銷。這麼做,需要追蹤吊扇的最後設定速度,如果undo方法被呼叫了,就要恢復成之前吊扇速度的設定值。就如下面這樣:

public class CeilingFanHighCommand implements Command {
    CeilingFan ceilingFan;
// 增加區域性狀態以便追蹤吊扇之前的速度
    int prevSpeed;
  
    public CeilingFanHighCommand(CeilingFan ceilingFan) {
        this.ceilingFan = ceilingFan;
    }
 
    public void execute() {
// 我們改變吊扇的速度之前,需要先將它之前的狀態記錄起來,以便需要撤銷時使用
        prevSpeed = ceilingFan.getSpeed();
        ceilingFan.high();
    }
 
// 將吊扇的速度設定會之前的值,達到撤銷的目的
    public void undo() {
        if (prevSpeed == CeilingFan.HIGH) {
            ceilingFan.high();
        } else if (prevSpeed == CeilingFan.MEDIUM) {
            ceilingFan.medium();
        } else if (prevSpeed == CeilingFan.LOW) {
            ceilingFan.low();
        } else if (prevSpeed == CeilingFan.OFF) {
            ceilingFan.off();
        }
    }
}

讓我們來測試下風扇吧

條件都具備了,那我們來測試下吧。我們打算把0號插槽的開啟按鈕設定為中速,把第1號插槽的開啟按鈕設定成高速,程式碼如下:

public class RemoteLoader {
 
    public static void main(String[] args) {
        RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();

        CeilingFan ceilingFan = new CeilingFan("Living Room");
   
        CeilingFanMediumCommand ceilingFanMedium = 
                new CeilingFanMediumCommand(ceilingFan);
        CeilingFanHighCommand ceilingFanHigh = 
                new CeilingFanHighCommand(ceilingFan);
        CeilingFanOffCommand ceilingFanOff = 
                new CeilingFanOffCommand(ceilingFan);
  
        remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);
        remoteControl.setCommand(1, ceilingFanHigh, ceilingFanOff);
   
        // 首先,我們以中速開啟吊扇
        remoteControl.onButtonWasPushed(0);
        // 然後關閉
        remoteControl.offButtonWasPushed(0);
        System.out.println(remoteControl);
        // 撤銷,應該會回到中速
        remoteControl.undoButtonWasPushed();
  
        // 這個時候開啟高速
        remoteControl.onButtonWasPushed(1);
        System.out.println(remoteControl);
        // 再進行一次撤銷,應該會回到中速
        remoteControl.undoButtonWasPushed();
    }
}

好了,至此我們不僅僅實現了單個的開與關,還實現了一整個遙控器所有控制元件的開與關,甚至是複雜的家電的開與關(音響、電扇的開啟略複雜),而且均實現了撤銷。作為程式設計師的你是不是經常使用撤銷功能呢,反正我是經常使用的噢。

但是,這還不是終極狀態。我們在這裡只能實現一個家電的開與關,如果光憑按下一個按鈕,不能實現燈光、電視、音響的同步使用,那這個遙控器對我們來說是不是還是有點low呢?是吧,確實有點low,如何破解,敬請期待我們的下一篇。

愛生活,愛學習,愛感悟,愛挨踢

相關推薦

設計模式命令模式

上一次留給大家去做的實踐,不知道大家執行的怎麼樣了呢。 我們通過一個簡單的練習,完成了一個控制開關。那現在,我們打算將遙控器的每個插槽,對應到一個命令這樣就要遙控器變成“呼叫者”。當按下按鈕,相應命令物件的execute()方法就會被呼叫,其結果就是,接收者(例如電燈、風扇、音響)的動作被呼叫。 實現遙控器

設計模式命令模式Command Pattern

摘要 命令模式(Command Pattern)是一種資料驅動的設計模式,它屬於行為型模式。請求以命令的形式包裹在物件中,並傳給呼叫物件。呼叫 介紹 意圖:將一個請求封裝成一個物件,從而使您可以用不同的請求對客戶進行引數化。 主要解決:在軟體系統中,行為請求者與行為實現者通常是一種緊

C++:設計模式命令模式例子

// 設計模式測試.cpp : 定義控制檯應用程式的入口點。 // 命令模式 #include "stdafx.h" #include <string> #include <iost

設計模式命令模式

在本次學習過程中,我們把封裝帶到一個全新的境界:把方法呼叫(method invocation)封裝起來。沒錯,通過封裝方法呼叫,我們可以把運算塊包裝成形。 所以呼叫此運算的物件不需要關心事情是如何進行的,只要知道如何使用包裝成形的方法來完成它就可以。通過封裝方法呼叫,也可以做一些很聰明的事情,例如記錄日誌,

設計模式命令模式

我回來啦!今天是週六,一看命令模式還有一個總結未完成,趕緊爬起來做做好。 就如上一篇所說的,如果擁有了一個遙控器,卻無法光憑按下一個按你,就同時能弄暗燈光、開啟音響和電視、設定到DVD,並讓熱水器開始加溫,那麼我要這個遙控器還有什麼意義呢? 使用巨集命令 根據比較高階的想法來看,就是我們需要製造一種新的命令,

設計模式問題集錦

是把 後繼 ogr data- 跟著 沒有 解釋器 space 基本實現 設計模式的主要資料是《大話設計模式》。第一階段先看看各種模式的基本概念。實現每一個模式下的樣例。然後在進行理解性的學習和掌握,靈活掌握各種模式的長處,知道某種模式適合那種狀態。如今,樣

《大話設計模式》——讀後感 8好菜每回味不同——建造者模式基礎案例1

ted builds src ret 分離 args 類圖 rec 方法 建造者模式:是將一個復雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。 建造者模式通常包括下面幾個角色: 1. builder:給出一個抽象接口,以規範產品對象的各個組成成分的

設計模式六大原則轉載

具體類 ima 傳遞參數 基礎上 比較 擁有 tex 至少 基類 關於設計模式的六大設計原則的資料網上很多,但是很多地方解釋地都太過於籠統化,我也找了很多資料來看,發現CSDN上有幾篇關於設計模式的六大原則講述的比較通俗易懂,因此轉載過來。   原作者博客鏈接:http:/

【Android架構】基於MVP模式的Retrofit2+RXjava封裝檔案下載

上篇中我們介紹了基於MVP的Retrofit2+RXjava封裝,還沒有看的點選這裡,這一篇我們來說說檔案下載的實現。 首先,我們先在ApiServer定義好呼叫的介面 @GET Observable<ResponseBody> downloadFile(@

設計模式簡單小例子結構型模式

簡單小例子 原始碼已經上傳到了GitHub. https://github.com/tanglonghui/DesignPatterns 設計模式簡單小例子(一) 建立型模式: https://blog.csdn.net/qq_40687864/article/details/810

Java設計模式總體簡介——簡單易懂

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程化,設計模式是

設計模式——java版》

第三章  建立型模式簡介 一、單例模式           1. 意思是:確保一個類只有一個例項,而且自行例項化並向整個系統提供這個例項          2. 適

設計模式裝飾者Decorator模式

首先來看一個場景,如圖: 工人分為很多種類,比如電工,管道工等等,同時又有A公司的電工,B公司的電工,A公司的管道工,B公司的管道工等等,那麼當有M個工種和N個公司的時候,就會有 M * N 個子類,這個繼承體系就會變得很龐大和複雜。那麼如何簡化呢,那麼

Javascript設計模式簡單工廠

建立型設計模式-簡單工廠模式 簡單工廠模式(Simple Factory):又稱之為靜態工廠模式,由一個工廠物件建立某一種產品物件類的例項。主要用來建立同一類物件。 多類單例項法 為了加深我們的理解,設定以下需求。假設一個大型超市賣各種東西,

設計模式六大原則例子-- 單一職責原則SRP例子

之前我們對設計模式的六大原則做了簡單歸納,這篇部落格是對單一職責原則進行的舉例說明。 單一職責原則的意義 物件不應該承擔太多職責,正如人不應該一心分為二用。唯有專注,才能保證物件的高內聚;唯有單一,才能保證物件的細粒度。物件的高內聚與細粒度有利於物件的

設計模式美學習:為何說要多用組合少用繼承?如何決定該用組合還是繼承?

在面向物件程式設計中,有一條非常經典的設計原則,那就是:組合優於繼承,多用組合少用繼承。為什麼不推薦使用繼承?組合相比繼承有哪些優勢?如何判斷該用組合還是繼承? 為什麼不推薦使用繼承? 繼承是面向物件的四大特性之一,用來表示類之間的 is-a 關係,可以解決程式碼複用的問題。雖然繼承有諸多作用,但繼承層次過深

設計模式美學習:業務開發常用的基於貧血模型的MVC架構違背OOP嗎?

我們都知道,很多業務系統都是基於 MVC 三層架構來開發的。實際上,更確切點講,這是一種基於貧血模型的 MVC 三層架構開發模式。 雖然這種開發模式已經成為標準的 Web 專案的開發模式,但它卻違反了面向物件程式設計風格,是一種徹徹底底的面向過程的程式設計風格,因此而被有些人稱為反模式(anti-patter

設計模式命令模式

能夠 ger 不同 exec cor del 需要 content ces 設計模式之命令模式 Feb 24, 2015 命令模式(Command)的定義是:用於將一個請求封裝成一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及執行可撤銷的

設計模式命令模式(Command)摘錄

single 而是 names 都得 結構 意圖 iterator nbsp 軟件 23種GOF設計模式一般分為三大類:創建型模式、結構型模式、行為模式。創建型模式抽象了實例化過程,它們幫助一個系統獨立於怎樣創建、組合和表示它的那些對象。一個類創建型模式使用繼承改變被實例

MVC實戰排球計分—— 構架概要設計

eight med 語言 fusion 詳情 插入數據 輸出數據 one 交互界面 本程序主要基於MVC4框架,使應用程序的輸入,處理和輸出強制性分開,使得軟件可維護性,可擴展性,靈活性以及封裝性得到提高, MVC應用程序分為三個核心部件:Model,View, Contr