【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!
命令模式:封装请求,轻松实现解耦!
大家好!今天我们来聊聊设计模式中的命令模式(Command Pattern)。如果你曾经需要将请求封装成对象,或者希望实现请求的撤销、重做等功能,那么命令模式就是你的不二之选!本文基于《Head First 设计模式》的命令模式章节,通过生动的故事和 Java 代码示例,带你轻松掌握命令模式的精髓。
1. 命令模式是什么?
命令模式是一种行为型设计模式,它将请求封装成对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录日志、撤销等操作。命令模式的核心思想是解耦请求的发送者和接收者,使得系统更加灵活和可扩展。
适用场景
- 需要将请求封装成对象,以便在不同的上下文中使用。
- 需要支持请求的撤销、重做、排队等功能。
- 需要解耦请求的发送者和接收者。
2. 命令模式的实现
故事背景
小明开发了一个智能家居系统,系统中有一个遥控器(RemoteControl)类,用于控制各种家电设备,比如灯(Light)、风扇(Fan)等。每个设备都有不同的操作,比如打开、关闭、调节亮度等。
问题出现
如果直接在遥控器中调用设备的方法,会导致遥控器和设备之间的耦合度过高。此外,如果需要支持撤销操作,代码会变得非常复杂。
解决方案:命令模式
小明决定使用命令模式,将每个操作封装成一个命令对象,从而解耦遥控器和设备。
代码实现
1. 定义命令接口
// 命令接口
interface Command {
void execute();
void undo();
}
2. 实现具体命令
// 具体命令:打开灯
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
// 具体命令:关闭灯
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
// 具体命令:打开风扇
class FanOnCommand implements Command {
private Fan fan;
public FanOnCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.on();
}
@Override
public void undo() {
fan.off();
}
}
// 具体命令:关闭风扇
class FanOffCommand implements Command {
private Fan fan;
public FanOffCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.off();
}
@Override
public void undo() {
fan.on();
}
}
3. 定义设备类
// 灯类
class Light {
public void on() {
System.out.println("Light is on");
}
public void off() {
System.out.println("Light is off");
}
}
// 风扇类
class Fan {
public void on() {
System.out.println("Fan is on");
}
public void off() {
System.out.println("Fan is off");
}
}
4. 实现遥控器
// 遥控器类
class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteControl() {
onCommands = new Command[2];
offCommands = new Command[2];
Command noCommand = new NoCommand();
for (int i = 0; i < 2; 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();
}
}
// 空命令类
class NoCommand implements Command {
@Override
public void execute() {
System.out.println("No command assigned");
}
@Override
public void undo() {
System.out.println("No command assigned");
}
}
5. 客户端代码
public class SmartHomeApp {
public static void main(String[] args) {
// 创建设备
Light livingRoomLight = new Light();
Fan livingRoomFan = new Fan();
// 创建命令
Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);
Command fanOn = new FanOnCommand(livingRoomFan);
Command fanOff = new FanOffCommand(livingRoomFan);
// 创建遥控器
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(0, lightOn, lightOff);
remoteControl.setCommand(1, fanOn, fanOff);
// 操作遥控器
remoteControl.onButtonWasPushed(0); // 输出: Light is on
remoteControl.offButtonWasPushed(0); // 输出: Light is off
remoteControl.undoButtonWasPushed(); // 输出: Light is on
remoteControl.onButtonWasPushed(1); // 输出: Fan is on
remoteControl.offButtonWasPushed(1); // 输出: Fan is off
remoteControl.undoButtonWasPushed(); // 输出: Fan is on
}
}
3. 命令模式的优点
-
解耦请求的发送者和接收者
命令模式将请求封装成对象,使得请求的发送者和接收者之间没有直接的依赖关系。 -
支持撤销和重做
通过实现undo()
方法,可以轻松实现撤销操作。 -
支持请求的排队和日志记录
命令对象可以被存储、传递和记录,从而支持请求的排队和日志记录。 -
易于扩展
新增命令时,只需实现新的命令类,无需修改现有代码。
4. 总结
命令模式通过将请求封装成对象,实现了请求的发送者和接收者之间的解耦,从而使得系统更加灵活和可扩展。通过本文的讲解和代码示例,相信你已经掌握了命令模式的核心思想和实现方法。在实际开发中,命令模式非常适合用于实现撤销、重做、排队等功能。
互动话题
你在项目中用过命令模式吗?遇到过哪些问题?欢迎在评论区分享你的经验!