【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!

news/2025/2/22 19:59:08

命令模式:封装请求,轻松实现解耦!

大家好!今天我们来聊聊设计模式中的命令模式(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. 命令模式的优点

  1. 解耦请求的发送者和接收者
    命令模式将请求封装成对象,使得请求的发送者和接收者之间没有直接的依赖关系。

  2. 支持撤销和重做
    通过实现 undo() 方法,可以轻松实现撤销操作。

  3. 支持请求的排队和日志记录
    命令对象可以被存储、传递和记录,从而支持请求的排队和日志记录。

  4. 易于扩展
    新增命令时,只需实现新的命令类,无需修改现有代码。


4. 总结

命令模式通过将请求封装成对象,实现了请求的发送者和接收者之间的解耦,从而使得系统更加灵活和可扩展。通过本文的讲解和代码示例,相信你已经掌握了命令模式的核心思想和实现方法。在实际开发中,命令模式非常适合用于实现撤销、重做、排队等功能。


互动话题
你在项目中用过命令模式吗?遇到过哪些问题?欢迎在评论区分享你的经验!


http://www.niftyadmin.cn/n/5862711.html

相关文章

多目标粒子群优化算法-MOPSO-(机器人路径规划/多目标信号处理(图像/音频))

具体完整算法请跳转&#xff1a;多目标粒子群优化算法-MOPSO-&#xff08;机器人路径规划/多目标信号处理&#xff08;图像/音频&#xff09;&#xff09; 多目标粒子群优化算法&#xff08;Multi-Objective Particle Swarm Optimization&#xff0c;MOPSO&#xff09;是一种基…

后“智驾平权”时代,谁为安全冗余和体验升级“买单”

线控底盘&#xff0c;正在成为新势力争夺下一个技术普及红利的新赛点。 尤其是进入2025年&#xff0c;比亚迪、长安等一线传统自主品牌率先开启高阶智驾的普及战&#xff0c;加上此前已经普及的智能座舱&#xff0c;舱驾智能的「科技平权」进一步加速行业启动「线控底盘」上车窗…

jmeter提取json中的多个返回值写入CSV文件供下一个接口调用(实操)

1、写一个线程&#xff0c;查询当前所有的病人数据 2、接口返回所有病人的数据后&#xff0c;下一个查询接口需要使用患者的床位与患者pid数据&#xff08;床位与pid一一对应 不重复&#xff09;。使用json提取器&#xff0c;提取接口返回值中的床位bedno、患者pid&#xff08;…

在Linux上创建一个Docker容器并在其中执行Python脚本

在Linux上创建一个Docker容器并在其中执行Python脚本的过程&#xff0c;涉及多个方面的内容&#xff0c;包括安装Docker、编写Dockerfile、构建镜像、运行容器等。 1. 安装Docker 在Linux上使用Docker之前&#xff0c;你需要确保系统已安装Docker。Docker支持的Linux发行版有…

pytorch预训练模型下载保存路径更改

正常情况下&#xff0c;torch预训练模型在线下载&#xff0c;下模型后的地址默认是&#xff1a; ~/.cache/torch/hub/checkpoints如果没有预先下载好预训练模型&#xff0c;在运行这个代码后&#xff0c;自动下载预训练模型的。 如果要更改路径&#xff0c;有两种办法&#x…

在windows下安装windows+Ubuntu16.04双系统(下)

这篇文章的内容主要来源于这篇文章&#xff0c;为正式安装windowsUbuntu16.04双系统部分。在正式安装前&#xff0c;若还没有进行前期准备工作&#xff08;1.分区2.制作启动u盘&#xff09;&#xff0c;见《在windows下安装windowsUbuntu16.04双系统(上)》 二、正式安装Ubuntu …

服务器socket端口绑定失败解决方案

一.服务器socket端口绑定失败 问题 在学习socket的使用&#xff0c;服务器使用的是libevent框架&#xff0c;绑定和监听的是服务器的私网ip以及8000端口号。 运行程序却输出打印信息&#xff1a;"bind error"。 //初始化监听 socket 并开始监听客户端连接 void S…

Python连接MySQL数据库完全指南

Python连接MySQL数据库完全指南 一、环境准备四部曲 1. 安装MySQL服务器&#xff08;Docker极简版&#xff09; docker run --name mysql2025 -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -d mysql:8.4 2. 安装Python连接驱动 # 官方推荐量子加密版 pip install mysql-con…