对命令模式的理解

目录

  • 一、场景
    • 1、文本编辑器并不是一个好的例子,设备控制器才是
    • 2、设备控制器的demo
  • 二、不用命令模式
    • 1、代码
    • 2、问题
  • 三、使用命令模式
    • 1、代码
    • 2、当需求变化时
      • 2.1 新增代码
      • 2.2 优点
  • 四、进一步思考
    • 1、省略对Command的建模可以吗?
    • 2、命令模式的价值

一、场景

  • 脱离场景谈设计模式毫无意义。先有某种场景,然后大神们(GoF)对其进行建模,以实现高内聚低耦合。

1、文本编辑器并不是一个好的例子,设备控制器才是

  • 看了不少讲命令模式的文章,举的例子基本都是文本编辑器。
  • 然而,并没有揭示命令模式的精髓。反而让读者觉得命令模式像过度设计。
  • 生活中,我们手机上会装一个设备控制器,里面既可以控制空调的开关,还可以控制电视的开关。我将通过这个例子来阐述命令模式到底解决了什么问题

2、设备控制器的demo

  • 用户选择空调,并选择关闭:空调便关闭了。
  • 用户选择电视机,并选择关闭:电视机便关闭了。

二、不用命令模式

1、代码

  • 设备:
public class AirConditioner {
    public void turnOn() {
        System.out.println("空调已开启");
    }

    public void turnOff() {
        System.out.println("空调已关闭");
    }
}

public class Television {
    public void turnOn() {
        System.out.println("电视已开启");
    }

    public void turnOff() {
        System.out.println("电视已关闭");
    }
}
  • 客户端:
public class Application {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        Television television = new Television();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            String device = scanner.next();
            if (device.equals("exit")) {
                break;
            }

            String command = scanner.next();
            if (device.equals("airConditioner")) {
                if (command.equals("turnOn")) {
                    airConditioner.turnOn();
                } else if (command.equals("turnOff")) {
                    airConditioner.turnOff();
                }
            } else if (device.equals("television")) {
                if (command.equals("turnOn")) {
                    television.turnOn();
                } else if (command.equals("turnOff")) {
                    television.turnOff();
                }
            }
        }
    }
}
  • 结果:
    在这里插入图片描述

2、问题

  • 随着发展,设备大概率不止空调和电视机,一旦增加新设备,客户端的代码就要有很大的改动。而且,每台设备能执行的命令也不止开启和关闭,一旦新增命令,又要修改客户端的代码。这都违背了“开闭原则”。
  • 另外,各种设备的代码杂糅在一起,违背了“单一职责原则”

三、使用命令模式

1、代码

  • 设备:
public interface Receiver {
    void turnOn();

    void turnOff();
}

public class AirConditioner implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("空调已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("空调已关闭");
    }
}

public class Television implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("电视已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("电视已关闭");
    }
}
  • 命令:
public abstract class Command {
    protected Receiver receiver;

    public void setReceiver(Receiver receiver) {
        this.receiver = receiver;
    }

    public abstract void execute();
}

public class TurnOffCommand extends Command {
    @Override
    public void execute() {
        receiver.turnOff();
    }
}

public class TurnOnCommand extends Command {
    @Override
    public void execute() {
        receiver.turnOn();
    }
}
  • 设备管理器:DeviceManager
public class DeviceManager {
    private final Map<String, Receiver> devices;
    private final Map<String, Command> commands;

    private DeviceManager() {
        devices = ImmutableMap.of(
                DeviceEnum.AIR_CONDITIONER.getValue(), new AirConditioner(),
                DeviceEnum.TELEVISION.getValue(), new Television()
        );

        commands = ImmutableMap.of(
                CommandEnum.TURN_ON.getValue(), new TurnOnCommand(),
                CommandEnum.TURN_OFF.getValue(), new TurnOffCommand()
        );
    }

    public static DeviceManager getSingleton() {
        return new DeviceManager();
    }

    public void execute(String device, String commandType) {
        Receiver receiver = devices.get(device);
        Command command = commands.get(commandType);

        command.setReceiver(receiver);
        command.execute();
    }
}
  • 客户端:
public class Application {
    public static void main(String[] args) {
        DeviceManager deviceManager = DeviceManager.getSingleton();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            String device = scanner.next();
            if (device.equals("exit")) {
                break;
            }

            String command = scanner.next();
            deviceManager.execute(device, command);
        }

    }
}

2、当需求变化时

  • 新增一个设备:洗衣机
  • 新增一个命令:待机

2.1 新增代码

  • 新增命令:
public class StandByCommand extends Command {
    @Override
    public void execute() {
        receiver.standby();
    }
}
  • 新增设备:
public interface Receiver {
    ...

    void standby(); // 新增方法
}

// 新增设备
public class Washer implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("洗衣机已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("洗衣机已关闭");
    }

    @Override
    public void standby() {
        System.out.println("洗衣机已待机");
    }
}
  • 修改设备(新增方法):
public class AirConditioner implements Receiver {
    ...

    @Override
    public void standby() {
        System.out.println("空调已待机");
    }
}

public class Television implements Receiver {
    ...

    @Override
    public void standby() {
        System.out.println("电视已待机");
    }
}
  • 修改设备管理器:
public class DeviceManager {
    private final Map<String, Receiver> devices;
    private final Map<String, Command> commands;

    private DeviceManager() {
        devices = ImmutableMap.of(
                ...
                DeviceEnum.WASHER.getValue(), new Washer()
        );

        commands = ImmutableMap.of(
                ...
				CommandEnum.STAND_BY.getValue(), new StandByCommand()
        );
    }
	
	...
}

2.2 优点

  • 之前写的代码,一行都没改动。只是通过新增代码,便实现了需求:
    在这里插入图片描述
    • 尽可能符合“开闭原则”。
  • 各个设备、各个命令都很单一,尽可能符合“单一职责”。

四、进一步思考

1、省略对Command的建模可以吗?

  • 省略后的伪代码:
public class DeviceManager {
	...
	public void execute(String device, String commandType) {
		// 根据device找到对应的实体
	 	Device realDevice =	deviceMap.get(device);
		
		// 每个device根据commandType执行不同的逻辑
		realDevice.execute(commandType);
	}
	...
}

// 各个device都要实现这样的路由逻辑:
public void execute(String commandType) {
	if ("turnOn".equals(commandType)) {
		...
	} else if ("turnOff".equals(commandType)) {
		...
	} else {
		...
	}
}
  • 显然,去除对Command的建模后,代码变得冗余了。

2、命令模式的价值

  • 当对真实场景建模后,各部分的交互逻辑如下图所示,便可以采用命令模式实现“高内聚、低耦合”的代码设计。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/591946.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

wpf转换器

WPF&#xff08;Windows Presentation Foundation&#xff09;中的转换器主要是指IValueConverter接口的实现&#xff0c;它用于在数据绑定过程中转换源数据和目标数据的类型或表示形式。这种机制使得开发者能够灵活地处理数据&#xff0c;特别是在用户界面&#xff08;UI&…

知识图谱需求

文章目录 公共安全数字经济金融科技资源优化科学研究制造业转型公共健康人文发展 公共安全 公共安全领域信息化以现代通信、网络、数据库技术为基础&#xff0c;将所研究对象各要素汇总至数据库&#xff0c;并针对各个业务领域进行定制化开发&#xff0c;以满足公共安全实战需求…

吴恩达机器学习笔记 三十七 电影推荐系统 使用特征 成本函数 协同过滤算法

以电影评分系统为例&#xff0c;令 r(i, j) 来表示用户 j 已经对电影 i 评分&#xff0c; y&#xff08;i, j&#xff09;表示评分具体是多少。 假如每部电影有自己的特征&#xff0c;那么用户 j 对电影 i 的评分预测为 w(j) * x(i) b(j) r(i, j) &#xff1a;一个用户 j 是否…

P9422 [蓝桥杯 2023 国 B] 合并数列

P9422 [蓝桥杯 2023 国 B] 合并数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 用队列即可 当两个队列队首&#xff1a;a b &#xff0c;弹出 当a < b&#xff0c;把a加给其后一个元素&#xff0c;弹出a 当b < a&#xff0c;把b加给其后一个元素&#xff0c;弹出…

BUUCTF---misc---菜刀666

1、下载附件&#xff0c;在wireshark中分析 2、题目说是菜刀&#xff0c;联想到http协议的post方法 3、使用命令过滤 http.request.methodPOST 4、打开数据包&#xff0c;发现有个不一样 这里面有一大串的数据包 5、追踪http数据流&#xff0c;发现z2后面是一个jpg文件的文件…

计算机——磁盘

磁盘介绍 磁盘&#xff08;Disk&#xff09;是计算机存储设备的一种&#xff0c;用于持久存储和读取数据。它以圆盘状的物理结构为基础&#xff0c;通过磁性材料在盘片上制造磁道和磁点&#xff0c;利用磁头来读写数据。 磁盘分类 磁盘的常见类型包括硬盘驱动器&#xff08;…

数据库基础--MySQL多表查询之联表查询

联表查询 定义&#xff1a;多张表联合在一起查询&#xff0c;例如学生信息与学生班级表、部门与员工表 创建两张表&#xff0c;主表与从表 CREATE TABLE TestMain(id INT Not NULL AUTO_INCREMENT,nameVARCHAR(10),introduction VARCHAR(255),PRIMARY KEY(id) ); CREATE TAB…

商超物联网方案-Hotspot Service和客流分析方案概述

商超物联网方案-Hotspot Service和客流分析方案概述 场景概述 大型商场、大型综合体在相互竞争及线上消费的影响下&#xff0c;利润增长缓慢&#xff0c;迫切需要通过提供个性化服务提升顾客购物体验&#xff0c;促进利润增长。 向不同顾客推送其感兴趣的广告&#xff0c;不仅…

opengauss概述-基础知识篇-备考华为高斯

目录 &#x1f9e8;考前准备: &#x1f3a1;数据库操作语言 ✨OLTP和OLAP &#x1f3af;常用函数 &#x1f9f2;字符处理函数 关于 left 和 right 特别重点的字符串函数 &#x1f9f2;数字操作函数 关于 ceil 和 floor &#x1f9f2;时间和日期处理函数 &#x1f9f…

自定义类型:联合体

1.联合体 首先我们还是要讲解一下什么是联合体&#xff1a; 联合体&#xff08;Union&#xff09;是一种特殊的数据结构&#xff0c;它允许在相同的内存位置存储不同的数据类型。联合体的大小等于其最大成员的大小&#xff0c;因为所有成员共享同一块内存空间。联合体的每个成…

【C语言】详解预处理

、 最好的时光&#xff0c;在路上;最好的生活&#xff0c;在别处。独自上路去看看这个世界&#xff0c;你终将与最好的自己相遇。&#x1f493;&#x1f493;&#x1f493; 目录 •✨说在前面 &#x1f34b;预定义符号 &#x1f34b; #define • &#x1f330;1.#define定义常…

Oracle对空值(NULL)的 聚合函数 排序

除count之外sum、avg、max、min都为null&#xff0c;count为0 Null 不支持加减乘除&#xff0c;大小比较&#xff0c;相等比较&#xff0c;否则只能为空&#xff1b;只能用‘is [not] null’来进行判断&#xff1b; Max等聚合函数会自动“过滤null” null排序默认最大&#xf…

【STL】map和set的原理及其使用

文章目录 关联容器键值对setset的介绍set的使用set的构造函数声明1&#xff1a;函数声明2&#xff1a;函数声明3&#xff1a; set的迭代器begin和endrbegin和rend set的容量empty()size&#xff08;&#xff09; set的修改操作inserteraseclearfindcount mapmap的介绍map的构造…

拼多多怎么推广才有效果

拼多多店铺的有效推广需要综合考虑多个方面&#xff0c;包括优化店铺信息、商品详情、参与平台活动、利用社交媒体、精准营销和客户服务等。具体如下&#xff1a; 拼多多推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自…

Go Web 开发【Gin 框架快速开发】

1、Gin Web 快速开发 1.1、环境准备 1.1.1、导入 gin 依赖 这里就叫 gin 依赖了&#xff0c;在 Goland 命令行中输入下面的命令&#xff1a; go get -u github.com/gin-gonic/gin 1.1.2、设置代理 如果下载失败&#xff0c;最好设置一下代理&#xff0c;在 cmd 命令行中输…

功能测试_分类_用例_方法

总结 测试分类 按阶段分类 是否查看源代码分类 是否运行分类 是否自动化 其他分类 软件质量模型 开发模型-瀑布模型 测试过程模型 v w 测试用例八大要素 用例编号 用例标题 …

海外仓系统:为什么对小型海外仓企业尤为重要,该怎么看待wms系统

相对于大型海外仓企业来说&#xff0c;小型海外仓受到资金和规模的限制&#xff0c;在库存管理、订单处理能力上面临的问题尤其大。而这正是海外仓系统擅长的地方&#xff0c;现代的海外仓系统逐渐发展以云端部署方式为主&#xff0c;这也为小型海外仓企业提供了很多便利。 1、…

基于Pytorch深度学习——GPU安装/使用

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

Java中的Lambda表达式

Lambda表达式的标准格式 格式&#xff1a;&#xff08;形式参数&#xff09;->{代码块} 形式参数&#xff1a;如果有多个参数&#xff0c;参数之间用逗号隔开 如果没有参数&#xff0c;留空即可 ->&#xff1a;由英文中画线和大于符号组成&#xff0c;固定写法。代表着…

学习中遇到的问题

1.UFUNCTION() 不是所有函数都能加UFUNCTION()修饰&#xff0c;涉及UE反射机制。 2.初始化用{} 初始化列表 3.创建C文件时修改了路径 这时.cpp文件会报错&#xff0c;只需删掉前面多余路径即可 4.函数的移除 1.虚幻5.1 UUserWidget不再包含OnLevelRemovedFromWorld() 转而使用…