開發之路(設計模式六:命令模式上)

封裝調用:將方法調用給封裝起來。

此次講的是命令模式,他的做用就是方法給封裝起來,有須要的時候就調用,調用者並不須要關心它是如何實現的。 編程

咱們來看一張流程圖
注:
一、訂單封裝了準備餐點的請求,
二、女服務員的工做是接受訂單,而後調用訂單的orderUp方法,女服務員並不關心訂單內容是什麼,她只需調用orderUp方法
三、廚師就是一個對象,他是真正知道餐點的具體內容的人,一旦女服務員調用orderUp方法,廚師就接手,實現餐點的具體方法,這裏廚師和女服務員是解耦的,訂單封裝了餐點的細節,她只要調用每一個訂單的方法便可,而廚師看了訂單就知道該作些什麼餐點。設計模式

圖片描述

好的,我從新繪製了一張圖反映命令模式以下圖,流程與上圖相同。
圖片描述數組

OK,咱們基本瞭解了命令模式的流程怎樣
下面寫一個模擬遙控器打開電燈和門這麼個動做的Demo例子app

一、寫電燈和門的類ide

電燈測試

package Entity;

public class Light {
    public Light() {
    }

    public void on() {

        System.out.println("燈亮了");
    }

    public void off() {
        System.out.println("燈滅了");
    }
}

this

package Entity;

public class Door {
    public Door() {
    }

    public void OpenDoor() {
        System.out.println("門開了");
    }

    public void OffDoor() {
        System.out.println("門關了");
    }
    
    public void StopDoor(){
        System.out.println("門中止了");
    }
    
    public void DoorLightOn(){
        System.out.println("門裏的燈亮了");
    }
}

二、建立命令接口spa

package Interface;
/**
 * 命令接口
 * 
 * @author Joy
 * 
 */
public interface Command {
    // 命令執行方法
    public void execute();
}

三、實現一個打開電燈的命令設計

package Implements;

import Entity.Light;
import Interface.Command;

public class LightOnCommand implements Command {
    Light light;

    // 構造器中傳入某個電燈類型
    // 以便讓這個命令控制,而後記錄在light實例變量中,
    // 當調用execute時,light會根據類型不一樣執行不一樣燈亮方法
    public LightOnCommand(Light light) {
        this.light = light;
    }

    // 執行燈亮方法
    @Override
    public void execute() {
        light.on();
    }
}

門的實現方法日誌

package Implements;

import Entity.Door;
import Interface.Command;

public class DoorOpenCommand implements Command {
    Door door;

    public DoorOpenCommand(Door door) {
        this.door = door;
    }

    @Override
    public void execute() {
        door.OpenDoor();
    }
}

四、調用命令對象(遙控器)

package Control;

import Interface.Command;

/**
 * 簡單遙控器
 *至關於調用者
 * @author Joy
 * 
 */
public class SimpleRemoteControl {
    // 命令對象類型,至關於插槽控制着一個裝置
    Command slot;

    public SimpleRemoteControl() {
    }

    // 這個方法用來設置插槽控制的命令
    // 若是客戶須要改變遙控器按鈕的行爲,能夠屢次調用此方法
    public void setCommand(Command command) {
        slot = command;
    }

    // 執行方法
    public void buttonWasPressed() {
        slot.execute();
    }
}

五、測試類(使用遙控器)

package TestMain;

import Control.SimpleRemoteControl;
import Entity.Door;
import Entity.Light;
import Implements.DoorOpenCommand;
import Implements.LightOnCommand;

public class LightTestMain {
    public static void main(String[] args) {
        // 遙控器至關於命令的調用者,會傳入一個命令對象,能夠用來發送請求
        SimpleRemoteControl remote = new SimpleRemoteControl();
        // 建立一個電燈對象,此對象也就是請求中的接收者
        Light light = new Light();
        Door door = new Door();
        // 建立打開電燈動做的類,並傳入接收者(light)
        LightOnCommand lightOn = new LightOnCommand(light);
        DoorOpenCommand doorOpen = new DoorOpenCommand(door);
        // 命令傳給調用者(遙控器)
        remote.setCommand(lightOn);
        // 模擬按下按鈕
        remote.buttonWasPressed();
        remote.setCommand(doorOpen);
        remote.buttonWasPressed();
    }
}

效果圖
圖片描述

這就是個基本命令模式的使用算是牛刀小試了一下,

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

下面是這模式的類圖
圖片描述

接下來繼續模擬一個遙控器,只不過此次需求變得複雜了起來。
示例圖以下,
圖片描述
每一個遙控器的插槽都對應一個命令嗎,這樣遙控器就變爲了「調用者」,當按下按鈕,對應的命令對象的execute方法就會被調用,結果就是接收者(例如:電燈,天花板電扇,音響)的動做被調用。

代碼開始
一、實體類

電燈

package Entity;

public class Light {
    //所處位置
    String location;

    public Light(String location) {
        this.location = location;
    }
    public void on(){
        System.out.println(location+"燈亮了");
    }
    
    public void off(){
        System.out.println(location+"燈關了");
    }
}

音響

package Entity;

/**
 * 音響類
 * 
 * @author Joy
 * 
 */
public class Stereo {
    //location 一個地點變量
    String location;

    public Stereo(String location) {
        this.location = location;
    }
    
    public void on(){
        System.out.println(location+"音響啓動");
    }
    
    public void off(){
        System.out.println(location+"音響關閉");
    }
    
    public void setDVD(){
        System.out.println(location+"音響放一張DVD並播放");
    }
    
    public void setCD(){
        System.out.println(location+"音響放一張CD並播放");
    }
    public void setRadio(){
        System.out.println(location+"音響以收音機無線電形式播放");
    }
    
    public void setVolume(int volume) {        
        System.out.println(location + " 音響音量設置爲 " + volume);
    }        
}

電視

package Entity;

public class TV {
    String location;
    int channel;//電視頻道

    public TV(String location) {
        
        this.location = location;
    }
    
    public void TVOn(){
        System.out.println(location+"電視自動打開了");
    }
    public void TVOff(){
        System.out.println(location+"電視自動關閉了");
    }
    //電視調頻道
    public void setTVChannel(int channel){
        this.channel=channel;
        System.out.println(location+"電視自動調到"+channel+"頻道");
    }
}

二、建立命令接口對象

package Interface;
public interface Command {
    //執行
    public void execute();
    //撤銷
    public void undo();
}

三、實現遙控器類(調用者)

package Control;

import Implements.NoCommand;
import Interface.Command;

/**
 * 實現遙控器
 * 
 * @author Joy
 */
public class RemoteControl {
    // 此時遙控器要處理7個開關控制,使用數組
    Command[] onCommands = new Command[7];
    Command[] offCommands = new Command[7];
    // 撤銷固然要先知道以前的命令
    // 撤銷變量,用來追蹤最後被調用的命令
    Command undoCommand;

    // 初始化遙控器類,一開始都是無操做noCommand是一個無操做對象
    // 在測試輸出時,沒有被明確指明命令的插槽,其命令默認爲noCommand對象
    public RemoteControl() {
        Command noCommand = new NoCommand();
        for (int i = 0; i < onCommands.length; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
        undoCommand = noCommand;
    }

    /**
     * 
     * @param slot
     *            :插槽的位置(相似索引值)
     * @param onCommand
     *            :開的命令
     * @param offCommand
     *            :關的命令 這些命令被記錄在開關數組對應的插槽位置上,以便使用
     */
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    /**
     * 開關按鈕是對應的插槽位置負責調用對應的方法 
     * 遙控器上面開關按鈕的不一樣位置就能夠控制不一樣類型的燈
     * undoCommand:當按下遙控器按鈕時,咱們取得這個命令,並記錄在undoCommand裏
     * @param slot
     */
    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
        undoCommand=onCommands[slot];
    }

    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
        undoCommand=offCommands[slot];
    }
    //添加一個撤銷按鈕
    public void undoButtonWasPushed(){
        //撤銷
        undoCommand.undo();
    }

    // 打印每一個插槽和它對應的命令
    @Override
    public String toString() {
        StringBuffer sbf = new StringBuffer();
        sbf.append("\n======================遙控器======================\n");
        for (int i = 0; i < onCommands.length; i++) {
            sbf.append("[插槽" + i + "]" + onCommands[i].getClass().getName()
                    + "\t" + offCommands[i].getClass().getName() + "\n");
        }
    
        return sbf.toString();
    }
}

四、實現各個命令(7個)

package Implements;

import Entity.Light;
import Interface.Command;

public class LightOffCommand implements Command {
    //具體對象變量
    Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();

    }

    @Override
    public void undo() {
        light.on();
        
    }
}
package Implements;

import Entity.Light;
import Interface.Command;

public class LightOnCommand implements Command {
    // 具體對象變量
    Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    // 執行打開電燈方法
    @Override
    public void execute() {
        light.on();

    }

    // 撤銷操做,關閉電燈
    @Override
    public void undo() {
        light.off();

    }
}
package Implements;

import Interface.Command;

public class NoCommand implements Command {
    // 這是個無操做類,插槽內的類沒有實例化是就走這個對象
    @Override
    public void execute() {

    }

    @Override
    public void undo() {
        // TODO 自動生成的方法存根
        
    }
}
package Implements;

import Entity.Stereo;
import Interface.Command;

public class StereoOffCommand implements Command {
    // 具體對象變量
    Stereo stereo;

    public StereoOffCommand(Stereo stereo) {
        this.stereo = stereo;
    }

    public void execute() {
        stereo.off();
    }

    @Override
    public void undo() {
        stereo.on();
    }
}
package Implements;

import Entity.Stereo;
import Interface.Command;

/**
 * 音響的
 * 
 * @author Joy
 * 
 */
public class StereoOnWithCDCommand implements Command {
    // 具體對象變量
    Stereo stereo;

    public StereoOnWithCDCommand(Stereo stereo) {
        this.stereo = stereo;
    }

    // 具體實現方法(方法再去調用實現方法)
    public void execute() {
        stereo.on();
        stereo.setCD();
        stereo.setVolume(11);
    }

    @Override
    public void undo() {
    stereo.off();
        
    }
}
package Implements;

import Entity.TV;
import Interface.Command;

public class TVOffCommand implements Command {
    TV tv;

    public TVOffCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.TVOff();
    }

    @Override
    public void undo() {
        tv.TVOn();
    }
}
package Implements;

import Entity.TV;
import Interface.Command;

public class TVOnCommand implements Command {
    TV tv;

    public TVOnCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.TVOn();
        tv.setTVChannel(15);

    }

    @Override
    public void undo() {
        tv.TVOff();
    }
}

五、測試類

package TestMain;

import Control.RemoteControl;
import Entity.Light;
import Entity.Stereo;
import Entity.TV;
import Implements.LightOffCommand;
import Implements.LightOnCommand;
import Implements.StereoOffCommand;
import Implements.StereoOnWithCDCommand;
import Implements.TVOffCommand;
import Implements.TVOnCommand;

public class TestMain {
    public static void main(String[] args) {
        // 實例化遙控器
        RemoteControl remoteControl = new RemoteControl();
        // 實例化須要控制對象,並傳入房子位置
        Stereo stereo = new Stereo("客廳");
        Light light = new Light("客廳");
        TV tv = new TV("臥室");
        // 調用設備開關方法
        StereoOnWithCDCommand stereoOnWichCD = new StereoOnWithCDCommand(stereo);
        StereoOffCommand stereoOffWithCD = new StereoOffCommand(stereo);
        LightOnCommand lightOn = new LightOnCommand(light);
        LightOffCommand lightOff = new LightOffCommand(light);
        TVOnCommand tvOn = new TVOnCommand(tv);
        TVOffCommand tvOff = new TVOffCommand(tv);

        // 設置插槽位置(遙控器的哪一個按鈕對應哪一個設備開關)
        remoteControl.setCommand(0, lightOn, lightOff);
        remoteControl.setCommand(3, stereoOnWichCD, stereoOffWithCD);
        remoteControl.setCommand(5, tvOn, tvOff);
        // 輸出插槽位置
        System.out.println(remoteControl);
        // 按下開關
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(3);
        remoteControl.offButtonWasPushed(3);
        remoteControl.onButtonWasPushed(5);
        remoteControl.offButtonWasPushed(5);
    }
}

效果圖
圖片描述

+1~~~~
在這個實例當中我特地預留了撤銷的功能,讓咱們看看加上撤銷功能(undo)的遙控器是怎麼運行的把。
新建一個undoCommandTest類

package TestMain;

import Control.RemoteControl;
import Entity.Light;
import Implements.LightOffCommand;
import Implements.LightOnCommand;

public class undoCommandTest {
    public static void main(String[] args) {
        // 實例化遙控器
        RemoteControl remoteControl = new RemoteControl();
        // 實例化須要控制對象,並傳入房子位置
        Light light = new Light("客廳");

        // 調用設備開關方法
        LightOnCommand lightOn = new LightOnCommand(light);
        LightOffCommand lightOff = new LightOffCommand(light);

        // 設置插槽位置(遙控器的哪一個按鈕對應哪一個設備開關)
        remoteControl.setCommand(0, lightOn, lightOff);

        // 按下開關
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        // 輸出插槽位置
        System.out.println(remoteControl);
        // 撤銷
        System.out.println("按下撤銷按鈕");
        remoteControl.undoButtonWasPushed();
        System.out.println("");
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(0);
        System.out.println(remoteControl);
        System.out.println("按下撤銷按鈕");
        remoteControl.undoButtonWasPushed();
    }
}

效果圖
圖片描述
看來撤銷的功能也OK,我腦中甚至浮現出在JavaWeb裏這個撤銷的效果了,23333。

感謝你看到這裏,命令模式的上部分到這裏就結束了,本人文筆隨便,如有不足或錯誤之處望給予指點,90度彎腰~~~很快我會發布命令模式下的內容,生命不息,編程不止!

參考書籍:《Head First 設計模式》
相關文章
相關標籤/搜索