設(shè)計模式系列—命令模式

《Head First設(shè)計模式》讀書筆記

命令模式(封裝調(diào)用)

一,場景介紹

1,需求

設(shè)計一個家電自動化遙控器的API,這個遙控器有7個可編程的插槽,每個插槽都有對應(yīng)的開和關(guān)按鈕,這個遙控器還有一個整體的撤銷按鈕。
  提供的家電廠商類有很多:Light、Door、TV等

2,思考

  • 對于遙控器,它只是知道按下按鈕,然后執(zhí)行對應(yīng)的ON或者OFF命令,但是他不知道電器執(zhí)行的具體細節(jié)。
  • 如果采用判斷語句if,else來逐個判斷,這樣必然違背了設(shè)計的基本思想。
  • 命令模式剛好符合這個場景,命令模式可以將動作請求(命令)從對象執(zhí)行對應(yīng)的操作完全解耦出來,那么學(xué)習(xí)和理解命令模式就是接下來的重點。

二,介紹命令模式

1,餐廳訂餐來理解命令模式

客戶(Client)點餐,然后服務(wù)員(Invoker)將點餐內(nèi)容記錄下來形成一個訂單(Command),然后服務(wù)員將訂單交給廚房(Receiver),然后廚房做出餐點。首先需要理解的是,作為調(diào)度者的服務(wù)員,他只需要形成訂單(也就是一個家電控制命令),然后將訂單送出去,并不需要了解訂單的內(nèi)容是什么,也不需要由哪個廚師來完成,他只是簡單的進行一個傳遞工作。這就是命令模式中一個重要的思想解耦。

2,命令模式中需要學(xué)習(xí)的重點

在命令模式中,我們不僅要學(xué)習(xí)命令模式的基本思想結(jié)構(gòu),我們還要學(xué)習(xí),執(zhí)行基本的單個命令模式,執(zhí)行多組的命令模式,命令模式的撤銷(回退到上一個狀態(tài))。最后要知道的就是命令模式的用途。

3,先實行一個基本的單個命令模式

先定義一個命令接口,可以讓遙控器設(shè)置接口對象的時候都是統(tǒng)一的類型。

public interface Command {
    public void execute();
}

定義一個Light類,這個是目標(biāo),里面設(shè)置的有Light.on()方法。

public class Light {
    public Light(){}
    public void on(){
        System.out.println("Light is on!");
    }
}

設(shè)置一個開燈的命令來包裝一下Light對象

public class LightOnCommand implements Command {
    private Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    @Override
    public void execute() {
        light.on();
    }
}

設(shè)置一個遠程命令控制接口,注意思考為什么要設(shè)置真么一個控制接口

public class SimpleRemoteControl {
    private Command comd;
    public SimpleRemoteControl() {  
    }
    public void setCommand(Command command){
        this.comd = command;
    }
    public void buttonWaspressed(){
        comd.execute();
    }
}

最后一個測試類

public class Test {
    public static void main(String[] args) {
        SimpleRemoteControl control = new SimpleRemoteControl();
        Light light = new Light();
        LightOnCommand lightCommand = new LightOnCommand(light);
        control.setCommand(lightCommand);
        control.buttonWaspressed();
    }
}

三,命令模式的結(jié)構(gòu)圖

如下:

Paste_Image.png

這個結(jié)構(gòu)圖里面多了一個LightOffCommand,其添加方式和上面的簡單設(shè)計模式實現(xiàn)代碼是一樣的,而且你還可以添加更多的TVCommand、DoorCommand等,這個可以自己實現(xiàn)。其實到這里我們已經(jīng)掌握了命令設(shè)計模式了,下面只是對命令模式的一些擴展用法介紹,便于在實際開發(fā)中靈活擴展。

四,命令的撤回

命令撤回,關(guān)于撤回,我們可以聯(lián)想一下我們?nèi)粘V蠧trl+Z操作,我們很容易想到將命令放到一個堆棧里面,我們只需要挨個彈出來即可實現(xiàn)。
  當(dāng)然,我們?nèi)匀豢梢园凑諘械陌咐齺硎煜ひ幌旅钅J健?/p>

1,案例介紹

比如吊扇(CeilingFan)他有高速、中速、低速、和關(guān)閉狀態(tài)。沃恩需要記住電扇之前的運行狀態(tài),可能是上面四種的任意一個,比如低速,現(xiàn)在我們執(zhí)行電扇,啟動高速,撤回(undo)的時候就變?yōu)榈退?。下面請看代碼

2,還是一樣,先創(chuàng)建一個Command接口

public interface Command {
    public void execute();
    public void undo();
}

3,創(chuàng)建一個CeilingFan類

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;
int speed;

public CeilingFan() {
    speed = OFF;
}
public void high(){
    speed = HIGH;
    System.out.println("The speed of the ceilingFan is High");
}
public void medium(){
    speed = MEDIUM;
    System.out.println("The speed of the ceilingFan is medium");
}
public void low(){
    speed = LOW;
    System.out.println("The speed of the ceilingFan is low");
}
public void off(){
    System.out.println("The speed of the ceilingFan is off");
    speed = OFF;
}
public int getSpeed(){
    return speed;
}
}

4,創(chuàng)建一個CeilingFanHighCommand,當(dāng)然你可以加上MediumCommand、LowCommand。其中undo就是一個撤回命令。

public class CeilingFanHighCommand implements Command{
private CeilingFan ceilingFan;
private int preSpeed;

public CeilingFanHighCommand(CeilingFan ceilingFan) {
    this.ceilingFan = ceilingFan;
}

@Override
public void execute() {
    preSpeed = ceilingFan.getSpeed();
    ceilingFan.high();
}

@Override
public void undo() {
    if(preSpeed == CeilingFan.HIGH){
        ceilingFan.high();
    }else if(preSpeed == CeilingFan.MEDIUM){
        ceilingFan.medium();
    }else if(preSpeed == CeilingFan.LOW){
        ceilingFan.low();
    }else if(preSpeed == CeilingFan.OFF){
        ceilingFan.off();
    }
}   
}

5,創(chuàng)建RemoteControl類

public class RemoteControl implements Command{
Command comd;
public RemoteControl() {}
public void setCommand(Command command){
    this.comd = command;
}
@Override
public void execute() {
    comd.execute();
}

@Override
public void undo() {
    comd.undo();
}
}

6,創(chuàng)建測試類

public class Test {
public static void main(String[] args) {
    RemoteControl control = new RemoteControl();
    CeilingFan ceilingFan = new CeilingFan();
    CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
    control.setCommand(ceilingFanHighCommand);
    control.execute();
    control.undo();
}
}

五,運行一組命令

有時候,在特使情景的時候,我們可能需要運行一組命令,比如我們回家,我們想一鍵完成開門,開燈,然后打開電視等著看劇。或者更多復(fù)雜命令的組合要求一鍵同時執(zhí)行。下面只實現(xiàn)同時開門,開燈,開電視。
  不多說,直接上代碼:

1,Command接口統(tǒng)一類型

public interface Command {
public void execute();
}

2,幾個實體類

public class Light {
public void on(){
    System.out.println("Light is on!");
}
}

public class Door {
public void open(){
    System.out.println("The door is openning!");
}
}

public class TV {
public void on(){
    System.out.println("the TV is on!");
}
}

3,幾個實體類對應(yīng)的打開命令類

public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
    this.light = light;
}
@Override
public void execute() {
    light.on();
}
}

public class DoorOpenCommand implements Command{
private Door door;
public DoorOpenCommand(Door door) {
    this.door = door;
}
@Override
public void execute() {
    door.open();
}
}

public class TVOnCommand implements Command{
private TV tv;
public TVOnCommand(TV tv) {
    this.tv = tv;
}
@Override
public void execute() {
    tv.on();
}
}

4,新添加一個AllOnCommand類,注意此處,本來可以硬編碼進去,但是為什么沒有,體會設(shè)計模式的基本原則。

public class AllOnCommand implements Command{
private Command[] command;
public AllOnCommand(Command[] command) {
    this.command = command;
}
@Override
public void execute() {
    for(Command command:command){
        command.execute();
    }
}
}

5,測試類

public class Test {
public static void main(String[] args) {
    SimpleRemoteControl control = new SimpleRemoteControl();
    Light light = new Light();
    Door door = new Door();
    TV tv = new TV();
    LightOnCommand lightCommand = new LightOnCommand(light);
    DoorOpenCommand doorOpenCommand =new DoorOpenCommand(door);
    TVOnCommand tvCommand = new TVOnCommand(tv);
    Command[] commands ={lightCommand,doorOpenCommand,tvCommand};
    AllOnCommand allCommand = new AllOnCommand(commands);
    control.setCommand(allCommand);
    control.buttonWaspressed();
}
}

六,總結(jié)

命令模式主要就是要體會封裝調(diào)用的思想,在實際開發(fā)中,消息隊列是一個很常用的一個機制,他們不管隊列里面是執(zhí)行什么任務(wù),網(wǎng)絡(luò)請求吶或者數(shù)據(jù)運算吶,完全不用管具體業(yè)務(wù),只需要執(zhí)行命令就可以。比如會用在數(shù)據(jù)庫日志中,通過記錄命令來達到日志的各種功能,比如數(shù)據(jù)庫恢復(fù)。等等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容