对命令模式的理解

news2024/11/24 1:03:29

目录

  • 一、场景
    • 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、命令模式的价值

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

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

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

相关文章

香港理工大学内地事务总监陆海天教授确认出席“边缘智能2024 - AI开发者峰会”并发表主题演讲

隨著AI技術的日新月異&#xff0c;我們正步入一個邊緣計算智能化與分布式AI相互融合的新紀元。這一變革不僅推動了分布式智能創新應用的飛速發展&#xff0c;還使得邊緣智能——這一結合邊緣計算和智能技術的新興領域&#xff0c;逐漸成為引領AI發展的重要力量。通過其分布式和…

# 从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;八&#xff09;Sentinel&#xff08;1&#xff09; 一、sentinel&#xff1a;概述 1、前言 – 服务熔断 Hystrix 的替换方案。 1&#xff09;2018年底 Netflix 官方宣布 Hystrix 已经足够稳定&#xff0c;不再积极开发 Hys…

基于AT89C52单片机的智能热水器控制系统

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/89242443?spm1001.2014.3001.5503 C 源码仿真图毕业设计实物制作步骤05 题 目 基于单片机的智能热水器系统 学 院 专 业 班 级 学 号 学生姓名 指导教师 完成日期…

【Docker学习】docker version查看版本信息

就像很多应用一样&#xff0c;docker也使用version来查看版本信息。但因为docker包含有不少独立组件&#xff0c;version的作用范围会更广一些。 用法1&#xff1a; docker --version 描述&#xff1a; 输出安装的Docker CLI 的版本号。关于Docker CLI&#xff0c;请访问。 实操…

Ubuntu启动后进入GRUB故障-Minimal BASH like line editing is supported.

目录 1.问题描述 2.解决方案 2.1 临时性办法 2.2 工具永久性修复 总结 1.问题描述 PC安装Ubuntu系统第二天重启后提示GUN GRUB version 2.04&#xff0c;之前是WindowsOS装Ubuntu后无法进入图形界面。具体原因据网友提供线索据说是由于在Windows上进行更新/重装/修改了引…

Golang | Leetcode Golang题解之第66题加一

题目&#xff1a; 题解&#xff1a; func plusOne(digits []int) []int {n : len(digits)for i : n - 1; i > 0; i-- {if digits[i] ! 9 {digits[i]for j : i 1; j < n; j {digits[j] 0}return digits}}// digits 中所有的元素均为 9digits make([]int, n1)digits[0]…

Docker-Compose编排LNMP并部署WordPress

前言 随着云计算和容器化技术的快速发展&#xff0c;使用 Docker Compose 编排 LNMP 环境已经成为快速部署 Web 应用程序的一种流行方式。LNMP 环境由 Linux、Nginx、MySQL 和 PHP 组成&#xff0c;为运行 Web 应用提供了稳定的基础。本文将介绍如何通过 Docker Compose 编排 …

【深度学习】第一门课 神经网络和深度学习 Week 3 浅层神经网络

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

20240503解决Ubuntu20.04和WIN10双系统下WIN10的时间异常的问题

20240503解决Ubuntu20.04和WIN10双系统下WIN10的时间异常的问题 2024/5/3 9:33 缘起&#xff1a;因为工作需要&#xff0c;编译服务器上都会安装Ubuntu20.04。 但是因为WINDOWS强悍的生态系统&#xff0c;偶尔还是有必须要用WINDOWS的时候&#xff0c;于是也安装了WIN10。 双系…

AJ-Report开源数据大屏 verification;swagger-ui RCE漏洞复现

0x01 产品简介 AJ-Report是一个完全开源的BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。多数据源支持,内置mysql、elasticsearch、kudu等多种驱动,支持自定义数据集省去数据接口开发,支持17+种大屏组件,不会开发,照着设计稿也可以制作大屏。三…

Linux进程——Linux下常见的进程状态

前言&#xff1a;在进程学习这一块&#xff0c;我们主要学习的就是PCB这个进程控制块&#xff0c;而PBC就是用来描述进程的结构体&#xff0c;而进程状态就是PCB结构体中的一个变量。 本篇主要内容&#xff1a; 操作系统中的进程状态Linux下的进程状态 在开始之前&#xff0c;我…

MLP手写数字识别(2)-模型构建、训练与识别(tensorflow)

查看tensorflow版本 import tensorflow as tfprint(Tensorflow Version:{}.format(tf.__version__)) print(tf.config.list_physical_devices())1.MNIST的数据集下载与预处理 import tensorflow as tf from keras.datasets import mnist from keras.utils import to_categori…

排序算法--直接选择排序

前提&#xff1a; 选择排序&#xff1a;选择排序(Selection sort)是一种比较简单的排序算法。它的算法思想是每一次从待排序的数据元素中选出最小(或最大)的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完。 话不多说&#xff0c;直接放图…

Xamarin.Android项目显示Properties

在 Visual Studio 2022 中&#xff0c;如果您需要调出“Properties”&#xff08;属性&#xff09;窗口&#xff0c;您可以使用以下几种方法&#xff1a; 快捷键&#xff1a; 您可以按 F4 快速打开当前选择项的“Properties”窗口。

postman中百度preview无法加载的解决方案

问题 在使用postman关联时&#xff0c;百度接口与天气接口已使用glb_city关联&#xff0c;但在百度接口发送请求时&#xff0c;发现preview无法加载 解决方案 1、进入百度 百度全球领先的中文搜索引擎、致力于让网民更便捷地获取信息&#xff0c;找到所求。百度超过千亿的中…

【云原生】Docker 实践(二):什么是 Docker 的镜像

【Docker 实践】系列共包含以下几篇文章&#xff1a; Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用Docker 实践&#xff08;二&#xff09;&#xff1a;什么是 Docker 的镜像Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerf…

在M1芯片安装鸿蒙闪退解决方法

在M1芯片安装鸿蒙闪退解决方法 前言下载鸿蒙系统安装完成后&#xff0c;在M1 Macos14上打开闪退解决办法接下来就是按照提示一步一步安装。 前言 重新安装macos系统后&#xff0c;再次下载鸿蒙开发软件&#xff0c;竟然发现打不开。 下载鸿蒙系统 下载地址&#xff1a;http…

eNSP-抓包解析HTTP、FTP、DNS协议

一、环境搭建 1.http服务器搭建 2.FTP服务器搭建 3.DNS服务器搭建 二、抓包 三、http协议 1.HTTP协议&#xff0c;建立在FTP协议之上 2.http请求 3.http响应 请求响应报文参考&#xff1a;https://it-chengzi.blog.csdn.net/article/details/113809803 4.浏览器开发者工具抓包…

堆栈打印跟踪Activity的启动过程(基于Android10.0.0-r41),framework修改,去除第三方app的倒计时页面

文章目录 堆栈打印跟踪Activity的启动过程(基于Android10.0.0-r41)&#xff0c;framework修改&#xff0c;去除第三方app的倒计时页面1.打印异常堆栈2.去除第三方app的倒计时页面3.模拟点击事件跳过首页进入主页 堆栈打印跟踪Activity的启动过程(基于Android10.0.0-r41)&#x…

SpringCloud微服务项目创建流程

为了模拟微服务场景&#xff0c;学习中为了方便&#xff0c;先创建一个父工程&#xff0c;后续的工程都以这个工程为准&#xff0c;实用maven聚合和继承&#xff0c;统一管理子工程的版本和配置。 后续使用中只需要只有配置和版本需要自己规定之外没有其它区别。 微服务中分为…