对命令模式的理解

目录

  • 一、场景
    • 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 {@Overridepublic void turnOn() {System.out.println("空调已开启");}@Overridepublic void turnOff() {System.out.println("空调已关闭");}
}public class Television implements Receiver {@Overridepublic void turnOn() {System.out.println("电视已开启");}@Overridepublic 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 {@Overridepublic void execute() {receiver.turnOff();}
}public class TurnOnCommand extends Command {@Overridepublic 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 {@Overridepublic void execute() {receiver.standby();}
}
  • 新增设备:
public interface Receiver {...void standby(); // 新增方法
}// 新增设备
public class Washer implements Receiver {@Overridepublic void turnOn() {System.out.println("洗衣机已开启");}@Overridepublic void turnOff() {System.out.println("洗衣机已关闭");}@Overridepublic void standby() {System.out.println("洗衣机已待机");}
}
  • 修改设备(新增方法):
public class AirConditioner implements Receiver {...@Overridepublic void standby() {System.out.println("空调已待机");}
}public class Television implements Receiver {...@Overridepublic 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、命令模式的价值

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GDPU unity游戏开发 碰撞器与触发器

砰砰叫&#xff0c;谁动了她的奶酪让你的小鹿乱撞了。基于此&#xff0c;亦即碰撞与触发的过程。 碰撞器与触发器的区别 通俗点讲&#xff0c;碰撞器检测碰撞&#xff0c;触发器检测触发&#xff0c;讲了跟没讲似的。碰撞器是用来检测碰撞事件的&#xff0c;在unity中&#xff…

gateway中对返回的数据进行处理

gateway中对返回的数据进行处理 背景1.项目层次 背景 最近公司有个需求是对返回数据进行处理&#xff0c;比如进行数据脱敏。最后在gateway中进行处理。 1.项目层次 根据项目的结构&#xff0c;原本在菜单功能处有对于权限设计的url判断&#xff0c;所以在url后面加了一个正…

CGAL 网格测地线距离计算

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格测地线距离是指在三维网格模型中计算两点之间的最短路径距离,考虑了网格的拓扑结构和几何形状。与传统的欧几里德距离不同,测地线距离考虑了网格的曲面形状,因此更适用于描述三维空间中的距离。 二、实现代码…

docker常用容器启动命令

docker常用容器启动命令 mysql启动redis启动nginx配置文件&启动 mysql启动 docker run -itd --name mysql-test --restartalways -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 mysqlredis启动 docker run -itd --name redis-test --restartalways -p 6379:6379 redisnginx…

蓝桥杯练习系统(算法训练)ALGO-949 勇士和地雷阵

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 勇士们不小心进入了敌人的地雷阵&#xff08;用n行n列的矩阵表示&#xff0c;*表示某个位置埋有地雷&#xff0c;-表示某个…

yolov5-pytorch-Ultralytics训练+预测+报错处理记录

一、前言 玩一段时间大模型&#xff0c;也该回归一下图像识别。本项目用于记录使用基于Ultralytics的yolov5进行目标检测测试。为什么用Ultralytics呢&#xff1f;答案有3 1、其良好的生态&#xff0c;方便我们部署到其它语言和设备上。因此本次测试结论&#xff1a;大坑没有&…

技术周总结 2024.04.29-05.05

一、python的数据表处理 """ 删除 Doris库中某些表中无效的数据 """ import mysql.connector import socket import socks import pandas as pd import pymysql from sqlalchemy import create_engine, text import csv from datetime import da…

UE5 蓝图入门

基础节点创建&#xff1a; 常量&#xff1a; 按住 1 &#xff0c;点击鼠标左键&#xff0c;创建常量 二维向量&#xff1a; 按住 2 &#xff0c;点击鼠标左键&#xff0c;创建二维向量 三维向量&#xff1a; 按住 3 &#xff0c;点击鼠标左键 乘法&#xff1a; 按住 m 键…

基于node.js+css+html+mysql博客系统

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

【C++风云录】图形和动作捕捉库全面解析:从OpenPose到OpenCV

深入浅出&#xff1a;六大计算机视觉和动作捕捉库的教程和比较 前言 本文将为读者详细介绍六种在计算机视觉领域广泛使用的开源软件和SDK&#xff0c;包括OpenPose、Vicon SDK、Intel RealSense SDK、Microsoft Kinect SDK、PCL (Point Cloud Library)和OpenCV。我们会一一解…

Vue进阶之Vue项目实战(一)

Vue项目实战 项目搭建初始化eslint版本约束版本约束eslint配置 stylelintcspellcz-githusky给拦截举个例子 zx 项目搭建 node版本&#xff1a;20.11.1 pnpm版本&#xff1a;9.0.4 初始化 vue3最新的脚手架 pnpm create vite byelide-demo --template vue-ts pnpm i pnpm dev…

MIPS32 指令架构

指令格式 R 类型 说明&#xff1a; 用于寄存器和寄存器操作 参数说明: Op: 指令操作码Rs: 第一个源操作数寄存器号&#xff0c;参与运算使用Rd: 目的操作数寄存器号&#xff0c;保存结果使用Shamt: 位偏移量&#xff0c;仅在位移指令使用&#xff0c;在此直接置0Func: 指令函…

区块链 | IPFS:IPNS(入门版)

&#x1f98a;原文&#xff1a;IPFS 与 IPNS 啥关系&#xff1f; &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。这篇文章讲得太入门了&#xff0c;不涉及任何底层原理。 正文 随着 2019 年 12 月 12 日 Filecoin 测试网的上线&#xff0c;IPF…

深入 Django 模型层:数据库设计与 ORM 实践指南

title: 深入 Django 模型层&#xff1a;数据库设计与 ORM 实践指南 date: 2024/5/3 18:25:33 updated: 2024/5/3 18:25:33 categories: 后端开发 tags: Django ORM模型设计数据库关系性能优化数据安全查询操作模型继承 第一章&#xff1a;引言 Django是一个基于Python的开源…

安卓手机APP开发__媒体开发部分__分享声音的输入

安卓手机APP开发__媒体开发部分__分享声音的输入 目录 概述 安卓10之前的版本的行为 安卓10的行为 共享场景 小助手普通的APP 有可读取权的服务 普通的APP 两个普通的APP 语音电话 普通的APP 概述 声音的输入通常来自于内嵌的麦克风,还有外置的麦克网,或者是一个…

【C++】深入剖析C++11中右值引用和左值引用

目录 一、左值引用 && 右值引用 二、左值引用于右值引用的比较 三、 右值引用使用场景和意义 1、函数返回值 ①移动赋值 ②移动构造 2、STL容器插入接口 ​3、完美转发 一、左值引用 && 右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了…

【简单介绍下Lisp的学习历程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

计算机网络 3.1网络的拓扑结构

第三章 网络技术基础 第一节 网络的拓扑结构 一、认识拓扑结构 1.定义&#xff1a; ①网络节点和链路的几何图形&#xff1b; ②网络中网络单元的地理分布和互联关系的几何构型&#xff1b; ③通信子网的拓扑结构。 2.三要素&#xff1a;可靠性、扩充性、费用高低。 3.影…

react 子组件调用父组件方法,获取的数据不是最新值

目录 原因解决方法方法一、去掉 useCallback&#xff0c; 这样每次父组件触发刷新&#xff0c;就会刷新子组件方法二、或者通过监听 val&#xff0c;val 值改变来刷新函数方法三、在父组件中&#xff0c;把 val 作为 key 值&#xff0c;每次 val 变化强制触发更新 出现问题的代…

Mybatis之Sqlsession、Connection和Transaction三者间的关系

前言 最近在看Mybatis的源码&#xff0c;搜到这篇文章Sqlsession、Connection和Transaction原理与三者间的关系&#xff0c;debug之后发现有不少疑惑&#xff0c;于是按照原文整理了一下&#xff0c;记录下debug中的一些困惑点。 对于我们开发来讲&#xff0c;不管跟任何关系…