《Head First 設計模式》 學習筆記,碼雲同步更新中
如有錯誤或不足之處,請一定指出,謝謝~
目錄
設計原則
- “依賴倒置”原則
- 未完待續…
設計模式
命令模式(Command Pattern)
定義:
- 將一個請求封裝成一個對象,以便使用不同的請求、隊列或者日誌來參數化其他對象。也支持可撤銷功能。
結構
- Command:抽象命令
- ConcreteCommand:具體命令
- Invoker:調用者
- Receiver:接受者
- Client:客戶端
分析
命令模式的本質是對命令進行封裝,將命令發出和執行的責任分割開
- 使請求本身成爲一個對象,可以被儲存和傳遞
- 請求方和接收方獨立開,請求方不需要知道請求如何被接收,是否、何時被執行,以及是如何執行的
優點:
- 降低耦合度
- 新命令的加入很容易
- 輕鬆設計一個宏命令或命令隊列
- 請求可以撤銷和重播
缺點:
- 系統內會產生大量命令類
使用場景:
- 需要將請求發出方和執行方解耦,兩者間不直接交互
- 需要命令的撤銷和重播功能
- 需要宏命令功能
- 需要在不同的時間指定請求,將請求排隊和執行
案例
/**
* 命令接口
**/
public interface Command {
/**
* 執行命令
*/
public void execute();
/**
* 撤銷命令
*/
public void undo();
}
/**
* 有7組開關的遙控器
**/
public class RemoteControl {
private static final int BUTTON_SIZE = 7;
/**
* 用數組記錄遙控器上多個開關對應的命令
*/
Command[] onCommands;
Command[] offCommands;
/**
* 上一個命令
*/
Command lastCommand;
/**
* 構造方法中初始化數組
*/
public RemoteControl() {
onCommands = new Command[BUTTON_SIZE];
offCommands = new Command[BUTTON_SIZE];
// 空對象
Command noCommand = new NoCommand();
for (int i = 0; i < BUTTON_SIZE; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
lastCommand = noCommand;
}
/**
* 往遙控器開關上設置命令
*/
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* 開
* @param slot
*/
public void pushOnButton(int slot) {
onCommands[slot].execute();
lastCommand = onCommands[slot];
}
/**
* 關
* @param slot
*/
public void pushOffButton(int slot) {
offCommands[slot].execute();
lastCommand = offCommands[slot];
}
/**
* 撤銷
*/
public void pushUndoButton() {
lastCommand.undo();
}
/**
* 重寫toString方法,給出遙控器說明書
* @return
*/
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("\n-------- Remote Control --------\n");
for (int i = 0; i < BUTTON_SIZE; i++) {
stringBuffer.append("[slot " + i + "] " + onCommands[i].getClass().getName() + " / " + offCommands[i].getClass().getName() + "\n");
}
return stringBuffer.toString();
}
}
家電
/**
* 電燈
**/
public class Light {
public String tag;
public Light(String tag) {
this.tag = tag;
}
public void on() {
System.out.println("the " + tag + " light is on...");
}
public void off() {
System.out.println("the " + tag + " light is off...");
}
}
/**
* 電扇
**/
public class ElectricFan {
/**
* 位置標記,區分哪裏的電扇
*/
private String tag;
private int speed;
public static final int OFF = 0;
public static final int LOW = 1;
public static final int MEDIUM = 2;
public static final int HIGH = 3;
public ElectricFan(String tag) {
this.tag = tag;
}
public void speedHigh() {
speed = HIGH;
System.out.println("the " + tag + " fan is on high speed");
}
public void speedMedium() {
speed = MEDIUM;
System.out.println("the " + tag + " fan is on medium speed");
}
public void speedLow() {
speed = LOW;
System.out.println("the " + tag + " fan is on low speed");
}
public void turnOff() {
speed = OFF;
System.out.println("the " + tag + " fan is off");
}
public int getSpeed() {
return speed;
}
}
/**
* 音響
**/
public class Stereo {
/**
* 開機
*/
public void on(){
System.out.println("stereo is on...");
}
/**
* 播放CD
*/
public void playCD(){
System.out.println("start playing CD...");
}
/**
* 設置音量
*/
public void setVolume(int volume){
System.out.println("set the volume to " + volume);
}
/**
* 關機
*/
public void off() {
System.out.println("stereo is off...");
}
}
具體命令
/**
* 空命令
*
* 空對象本身也可以被當做一種設計模式。
* 可以用於當你不想返回一個有意義的對象時。
* 遙控器在最初不可能設置了有意義的命令對象,
* 使用空對象可以避免沒必要的報錯。
**/
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
/**
* 打開電燈的命令
**/
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();
}
}
// 關閉電燈 略
/**
* 電風扇 高風速 命令
**/
public class ElectricFanSpeedHighCommand implements Command {
ElectricFan fan;
/**
* 記錄之前的風速
*/
int prevSpeed;
public ElectricFanSpeedHighCommand(ElectricFan fan) {
this.fan = fan;
}
@Override
public void execute() {
// 記錄風速
prevSpeed = fan.getSpeed();
fan.speedHigh();
}
/**
* 撤銷,恢復之前的風速
*/
@Override
public void undo() {
if (prevSpeed == ElectricFan.HIGH) {
fan.speedHigh();
} else if (prevSpeed == ElectricFan.MEDIUM) {
fan.speedMedium();
} else if (prevSpeed == ElectricFan.LOW) {
fan.speedLow();
} else if (prevSpeed == ElectricFan.OFF) {
fan.turnOff();
}
}
}
// 其他電扇命令 略
/**
* 音響打開命令
**/
public class StereoOffWithCdCommand implements Command {
Stereo stereo;
public StereoOffWithCdCommand(Stereo stereo) {
this.stereo = stereo;
}
@Override
public void execute() {
stereo.off();
}
@Override
public void undo() {
stereo.on();
stereo.playCD();
stereo.setVolume(50);
}
}
// 音響關閉命令 略
/**
* 宏命令
**/
public class MacroCommand implements Command {
/**
* 儲存一組命令
*/
Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
/**
* 一鍵全部執行
*/
@Override
public void execute() {
for (int i = 0; i < commands.length; i++) {
commands[i].execute();
}
}
@Override
public void undo() {
for (int i = 0; i < commands.length; i++) {
commands[i].undo();
}
}
}
測試類
/**
* 遙控器測試
**/
public class RemoteControlTest {
public static void main(String[] args) {
RemoteControl control = new RemoteControl();
Light bedroomLight = new Light("bedroom");
Light kitchenLight = new Light("kitchen");
Stereo stereo = new Stereo();
ElectricFan livingRoomFan = new ElectricFan("livingRoom");
LightOnCommand bedroomLightOnCommand = new LightOnCommand(bedroomLight);
LightOffCommand bedroomLightOffCommand = new LightOffCommand(bedroomLight);
LightOnCommand kitchenLightOnCommand = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOffCommand = new LightOffCommand(kitchenLight);
StereoOnWithCdCommand stereoOnWithCdCommand = new StereoOnWithCdCommand(stereo);
StereoOffWithCdCommand stereoOffWithCdCommand = new StereoOffWithCdCommand(stereo);
ElectricFanSpeedHighCommand livingRoomFanSpeedHighCommand = new ElectricFanSpeedHighCommand(livingRoomFan);
ElectricFanOffCommand livingRoomFanOffCommand = new ElectricFanOffCommand(livingRoomFan);
control.setCommand(0, bedroomLightOnCommand, bedroomLightOffCommand);
control.setCommand(1, kitchenLightOnCommand, kitchenLightOffCommand);
control.setCommand(2, stereoOnWithCdCommand, stereoOffWithCdCommand);
control.setCommand(3, livingRoomFanSpeedHighCommand, livingRoomFanOffCommand);
System.out.println(control.toString());
// control.pushOnButton(1);
// control.pushUndoButton();
// control.pushOnButton(2);
// control.pushOffButton(2);
// control.pushUndoButton();
// control.pushOnButton(3);
// control.pushOffButton(3);
// control.pushUndoButton();
// 宏命令測試
Command[] turnOnAllLightsArr = {bedroomLightOnCommand, kitchenLightOnCommand};
Command[] turnOffAllLightsArr = {bedroomLightOffCommand, kitchenLightOffCommand};
MacroCommand turnOnAllLights = new MacroCommand(turnOnAllLightsArr);
MacroCommand turnOffAllLights = new MacroCommand(turnOffAllLightsArr);
control.setCommand(4, turnOnAllLights, turnOffAllLights);
control.pushOnButton(4);
control.pushOffButton(4);
control.pushUndoButton();
}
}