设计模式——命令模式
2019-08-08 本文已影响0人
prik丶
《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();
}
}