【HeadFirst 设计模式学习笔记】6 命令模式
1.這一節我們的任務是創建一個類似智能家居的萬能遙控器,控制各種家電。我們需要將“請求”封裝成對象(一個命令對象通過在特定接收者上綁定一組動作來封裝請求),以便使用不同的請求、隊列、或者日志來參數化其對象——這就是命令模式。
2.我們具體來看一個例子:
首先我們要完成對命令的對象封裝:
public interface Command {?
??? public void execute();?
}
只有一個方法,所有的具體命令的對象都要實現這個接口,這就做到了封裝,比如對于燈這個對象,
public class Light {
??? public Light() {?
??? }
??? public void on() {?
??????? System.out.println("Light is on");?
??? }
??? public void off() {?
??????? System.out.println("Light is off");?
??? }?
}
我們可以通過上述接口封裝“開燈”這個命令,這個就是所謂的命令對象,它把動作和接收者包進對象之中,只暴露一個execute方法:
public class LightOnCommand implements Command {?
??? Light light;?
??? public LightOnCommand(Light light) {?
??????? this.light = light;?
??? }?
??? public void execute() {?
??????? light.on();?
??? }?
}
而我們的遙控器對于上述封裝要一無所知,這樣才能做到解耦:
public class SimpleRemoteControl {?
??? Command slot;?
??? public SimpleRemoteControl() {}?
??? public void setCommand(Command command) {?
??????? slot = command;?
??? }?
??? 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();?
??????? }?
}
很簡單的,我們想要在一個遙控機中實現控制多個家電的能力就可以用一個數組來維護這樣的一組命令,可以看做提供了多個命令的插槽:
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;?
??? }?
??? public void onButtonWasPushed(int slot) {?
??????? onCommands[slot].execute();?
??? }?
??? public void offButtonWasPushed(int slot) {?
??????? offCommands[slot].execute();?
??? }?
??? public String toString() {?
??? }?
}
你可能會注意到一個叫做noCommand的對象出現在初始化遙控器對象時。這個是個小技巧,我們并不想每次都檢查某個插槽是不是都綁定了命令,那么我們就引入了這個東東,實現一個不做事情的命令:
public class NoCommand implements Command {?
??? public void execute() { }?
}
這樣初始化就讓每一個插槽都有命令了,以后若不進一步指明命令的插槽,那么就是默認這個noCommand對象。這就是一個典型的“空對象”例子,當你不想返回一個有意義的對象時,空對象就十分有用,此時空對象可以做成判斷NULL或者提示“未綁定”信息的工作。
3.我們在命令模式中也可以設置類似undo的撤銷命令來撤銷發出的命令請求:
public interface Command {?
??? public void execute();?
??? public void undo();?
}
我們還是拿開電燈這個操作來說明,畢竟這足夠簡單:對于一個開燈命令,其對應的撤銷命令自然是“關電燈”:
public class LightOnCommand implements Command {?
??? Light light;?
??? public LightOnCommand(Light light) {?
??????? this.light = light;?
??? }?
??? public void execute() {?
??????? light.on();?
??? }?
public void undo() {?
??????? light.off();?
??? }?
}
雖然這簡單之極,這還沒完,我們必須讓遙控器記住上次到底做了什么操作,才可能去撤銷:
package headfirst.command.undo;
import java.util.*;
//?
// This is the invoker?
//?
public class RemoteControlWithUndo {?
??? Command[] onCommands;?
??? Command[] offCommands;?
Command undoCommand;?
??? public RemoteControlWithUndo() {?
??????? 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;?
??????? }?
??????? undoCommand = noCommand;?
??? }?
??? public void setCommand(int slot, Command onCommand, Command offCommand) {?
??????? onCommands[slot] = onCommand;?
??????? offCommands[slot] = offCommand;?
??? }?
??? 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();//發出撤銷命令?
??? }?
??? public String toString() {?
??? }?
}
對于電燈,我們只有兩種狀態,但是要是多個狀態呢?我們舉一個吊扇的例子,吊扇有多個轉速,高中低關,四個狀態:
public class CeilingFan {?
??? String location = "";?
??? int level;?
??? public static final int HIGH = 2;?
??? public static final int MEDIUM = 1;?
??? public static final int LOW = 0;?
??? public CeilingFan(String location) {?
??????? this.location = location;?
??? }?
??? public void high() {?
??????? // turns the ceiling fan on to high?
??????? level = HIGH;?
??????? System.out.println(location + " ceiling fan is on high");?
??? }
??? public void medium() {?
??????? // turns the ceiling fan on to medium?
??????? level = MEDIUM;?
??????? System.out.println(location + " ceiling fan is on medium");?
??? }
??? public void low() {?
??????? // turns the ceiling fan on to low?
??????? level = LOW;?
??????? System.out.println(location + " ceiling fan is on low");?
??? }?
??? public void off() {?
??????? // turns the ceiling fan off?
??????? level = 0;?
??????? System.out.println(location + " ceiling fan is off");?
??? }?
??? public int getSpeed() {?
??????? return level;?
??? }?
}
那么在實現風扇各個轉速命令時,就要去記錄在執行這個命令前風扇的轉速,其撤銷命令中則根據記錄的部分完成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() {?
??????? switch (prevSpeed) {?
??????????? case CeilingFan.HIGH:???? ceilingFan.high(); break;?
??????????? case CeilingFan.MEDIUM: ceilingFan.medium(); break;?
??????????? case CeilingFan.LOW:???? ceilingFan.low(); break;?
??????????? default:???????????????? ceilingFan.off(); break;?
??????? }?
??? }?
}
當然你也可以實現諸如多個命令批量執行和完成多個撤銷操作。在這個例子中,還可以將電燈和電風扇等抽象為電器這個抽象類,然后啟開關等兩種電器的通用操作就可以寫一個命令類完成,而不需要單獨寫“開電燈”“開電扇”這樣的針對特定電器的命令類了。
命令模式可以用于工作隊列和日志操作等方面。
本文轉自gnuhpc博客園博客,原文鏈接:http://www.cnblogs.com/gnuhpc/archive/2012/12/21/2827482.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的【HeadFirst 设计模式学习笔记】6 命令模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 替代jquery1.9版本以前的togg
- 下一篇: Windows Phone 7 学习笔记