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

在本次學習過程當中,咱們把封裝帶到一個全新的境界:把方法調用(method invocation)封裝起來。沒錯,經過封裝方法調用,咱們能夠把運算塊包裝成形。java

因此調用此運算的對象不須要關心事情是如何進行的,只要知道如何使用包裝成形的方法來完成它就能夠。經過封裝方法調用,也能夠作一些很聰明的事情,例如記錄日誌,或者重複使用這些封裝來實現撤銷。讓咱們開始吧學習

如今有一個用戶A,他們家有不少家電,在裝修的時候,他讓裝修公司把這些家電當成了一個總體,想要經過一個遙控器就能控制家裏的電燈、風扇、熱水器、音響設備和其餘的相似的可控制裝置,這樣的話就顯得很高大上。測試

因此呢,他讓每一個廠商投提供了一組Java類,用來控制家電。手上又有一個遙控器,但願咱們可以建立一組控制遙控器的API,讓每一個插槽都能控制一個或一組裝置,即經過一個遙控器就能控制全部家電,是否是很酷。this

咱們先來看下廠商給的類。設計

這個類確實很多呀,並且接口也各有差別。還有更麻煩的,隨着家電數量的增長,這樣的類還會愈來愈多。那麼,如何設計一個遙控器API就變得頗有挑戰性了是吧。日誌

因此,命令模式應運而生了。在咱們的設計中,採用「命令模式」,利用命令對象,把請求(例如打開電燈)封裝成一個特定對象(例如客廳電燈對象)。因此,若是對每一個按鈕都存儲一個命令對象,那麼當按鈕被按下的時候,就能夠請命令對象作相關的工做。遙控器並不須要知道工做內容是什麼,只要有個命令對象能和正確的對象溝通,把事情作好就能夠了。這樣,遙控器和電燈對象都解耦了。code

可能把遙控器換成餐廳點餐,你們會更容易理解。我在這裏簡單描述下:首先顧客到了餐廳,根據菜單點了一部分菜,把菜單給服務員;服務員拿到了訂單,就提交到櫃檯上,並向後廚喊了一聲「xx號桌xx訂單」來了;後廚根據菜單進行配菜,燒菜。全稱服務員都不須要知道訂單具體內容是什麼,只要將桌上的客戶點的餐提供給後廚便可,這和咱們的遙控器就是一個道理了。對象

第一個命令對象

實現命令接口

那咱們就來建立咱們的命令對象吧。首先,咱們得讓全部的命令對象實現相同的包含一個方法的接口。在餐廳訂餐的例子上,就是建立訂單,咱們在程序的世界裏,取名叫作execute()blog

public interface Command {
    public void execute();
}
實現一個打開電燈的命令

如今,假設想實現一個打開電燈的命令。根據以前看到的廠商提供的類,Light類有兩個方法,on和off。接口

// 這是一個命令,因此須要實現Command接口
public class LightOnCommand implements Command {
    Light light;

// 構造器傳入某個電燈,以便讓這個命令控制,而後記錄在實例變量中
    public LightOnCommand(Light light) {
        this.light = light;
    }

// 這個execute方法調用接收對象的on方法
    public void execute() {
        light.on();
    }

}

使用命令對象

如今,咱們讓遙控器工做起來,先來點簡單的。假設遙控器只有一個按鈕和對應的插槽,能夠控制一個裝置:

public class SimpleRemoteControl {
// 有一個插槽持有命令,而這個命令控制着一個裝置
    Command slot;
 
    public SimpleRemoteControl() {}
 
// 這個方法用來設置插槽控制的命令
    public void setCommand(Command command) {
        slot = command;
    }
 
// 當按下按鈕時,這個方法就會被調用,使得當前命令銜接插槽,並調用它的execute方法
    public void buttonWasPressed() {
        slot.execute();
    }
}

就這樣咱們就能實現一個簡單的遙控器了。請看咱們的測試

public class RemoteControlTest {
    public static void main(String[] args) {
    // 遙控器就是調用者,會傳入一個命令對象,能夠用來發出請求
        SimpleRemoteControl remote = new SimpleRemoteControl();
    // 如今建立一個電燈對象,此對象也就是請求的接收者
        Light light = new Light();
    // 這裏建立一個命令,而後將接收者傳給它
        LightOnCommand lightOn = new LightOnCommand(light);
    
    // 把命令傳給調用者
        remote.setCommand(lightOn);
    // 模擬按下按鈕
        remote.buttonWasPressed();
    }
    
}

定義命令模式

通過訂餐流程的理解,以及剛纔這個小練習,相信你也對命令模式內的類 和對象如何互動理解得很清楚了吧。那咱們在這裏趁熱打鐵,定義一下命令模式。

命令模式將請求封裝成對象,以便使用不一樣的請求,隊列或者日誌來參數化其餘對象。命令模式也支持可撤銷的操做。

仔細想一想,咱們知道有一個命令對象經過在特定接收者上綁定一組動做來封裝一個請求。要達到這一點,命令對象將動做和接收者包進對象中。這個對象只暴露出一個execute()方法,當此方法被調用的時候,接收者就會進行這些動做。從外面來看,其餘對象不知道究竟哪一個接收者進行了哪些動做,只知道若是調用execute()方法,請求的目的就能達到。

讓咱們來看下命令模式的類圖:

好了,經過這個簡單的小練習,咱們知道如何控制電燈的開關了。在前面廠商給的類中,還有好多方法,好比電風扇、吊燈、電視機、音響等等。控制單個咱們已經能搞定了,那控制多個呢?是否是同理呢?仍是你有更好的方式呢?小編想請你先動動你的小手,咱們下次見分曉。

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

相關文章
相關標籤/搜索