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

上一次留給你們去作的實踐,不知道你們執行的怎麼樣了呢。程序員

咱們經過一個簡單的練習,完成了一個控制開關。那如今,咱們打算將遙控器的每一個插槽,對應到一個命令這樣就要遙控器變成「調用者」。當按下按鈕,相應命令對象的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,純粹就是簡單的開和關命令。那如今,咱們來爲音響編寫開與關的命令。app

音響的關閉是毫無難度,就是開啓的時候有點複雜,你知道爲何嗎?難道音響開了就行了?是否還須要後續其餘的動做才能讓音響響起來了?哎呀,小編多嘴了好像。學習

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);
	}
}
複製代碼

這裏列舉了一個電燈,一個音響,差很少就把其餘相似的都已經搞定了,好比電扇、門,對吧。因此,趕忙看看你以前動手的操做,是否是和小編的差很少。測試

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

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);
	}
}
複製代碼

寫文檔的時候到了

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

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

撤銷哪去了?

別急別急,小編說的功能都會有的。撤銷功能使用起來就是這樣的:好比說客廳的電燈是關閉的,而後你按下遙控器上的開啓按鈕,天然電燈就被打開了。如今若是按下撤銷按鈕,那麼上一個動做將被倒轉,在這個例子裏,電燈將被關閉。code

一樣,咱們先來一個簡單的撤銷示例。以前咱們用的是execute()方法實現開啓或者關閉的調用,那麼咱們用undo()方法來執行撤銷操做。即在Command接口裏實現一個同execute()相反的方法undo(),而後在實現類裏將undo()的動做作成和execute()相反的操做便可。cdn

講的有點籠統?在這裏小編就不提供具體的代碼了,詳細的請看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,如何破解,敬請期待咱們的下一篇。

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

相關文章
相關標籤/搜索