命令设计模式

简介

命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作:请求方发出请求要求执行一个操作;接收方收到请求,并执行操作。命令模式解耦了请求方和接收方,请求方只需请求执行命令,不用关心命令怎样被接收、怎样被操作及是否被执行等。命令模式属于行为型设计模式。

通用模板

  1. 创建接收者角色:该类负责具体实施或执行一个请求。

    // 接收者
    public class Receiver {public void action() {System.out.println("执行具体操作");}
    }
    
  2. 创建抽象命令角色:定义需要执行的所有命令行为。

    // 抽象命令接口
    public interface ICommand {void execute();
    }
    
  3. 创建具体命令角色:该类内部维护一个Receiver,在其execute()方法中调用Receiver的相关方法。

    // 具体命令
    public class ConcreteCommand implements ICommand {// 直接创建接收者,不暴露给客户端private Receiver receiver = new Receiver();@Overridepublic void execute() {this.receiver.action();}
    }
    
  4. 创建请求者角色:接收客户端的命令,并执行命令。

    // 请求者
    public class Invoker {private ICommand cmd;public Invoker(ICommand cmd) {this.cmd = cmd;}public void action(){this.cmd.execute();}
    }
    

模板测试

  1. 测试代码
    public class Client {public static void main(String[] args) {ConcreteCommand cmd = new ConcreteCommand();Invoker invoker = new Invoker(cmd);invoker.action();}
    }
    
  2. 测试结果
    执行具体操作
    

应用场景

在日常生活中,命令模式是很常见的。比如,经历过黑白电视机年代的小伙伴应该都有过这样的经历。那个年代在看电视的时候,想要换个频道简直不容易。我们得走到电视机前扭动换台的旋钮,一顿“咔咔咔”折腾才能完成频道的切换。如今,遥控器的发明简直就是“解放战争”,我们躺在沙发上只需要轻轻一按遥控器即可完成频道的切换。这就是命令模式,将换台请求和换台处理完全解耦了。
当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现,使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令接口呈现弱耦合(内部方法无须一致),具备良好的扩展性。命令模式主要适用于以下应用场景。 (1)现实语义中具备“命令”的操作(如命令菜单、Shell命令等)。
(2)请求的调用者和接收者需要解耦,使得调用者和接收者不直接交互。
(3)需要抽象出等待执行的行为,比如撤销(Undo)操作和恢复(Redo)等操作。
(4)需要支持命令宏(即命令组合操作)。

优点

(1)通过引入中间件(抽象接口),解耦了命令请求与实现。
(2)扩展性良好,可以很容易地增加新命令。
(3)支持组合命令,支持命令队列。
(4)可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

缺点

(1)具体命令类可能过多。
(2)命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量;代码抽离肯定比代码聚合更难理解。

“生搬硬套”实战

场景描述

我们可以用一个遥控器控制家用电器(如电灯)作为例子。在这个场景中,遥控器是发出命令的对象,而家用电器则是执行命令的对象。

代码开发
  1. 创建接收者角色(这里指的是实际执行的对象灯)

    // 定义接收者类,即实际执行动作的对象——Light
    public class Light {public void turnOn() {System.out.println("Light is on");}public void turnOff() {System.out.println("Light is off");}
    }
    
  2. 创建抽象命令角色:(这里指的是抽象命令接口,定义执行和取消操作)

    // 定义一个命令接口
    public interface ICommand {void execute();void undo();
    }
    
  3. 创建具体命令角色(这里指的是开灯和关灯的具体命令实现类)

    // 具体的开灯命令类
    public class LightOnCommand implements ICommand{private final Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}@Overridepublic void undo() {light.turnOff();}
    }
    
    // 具体关灯命令类
    public class LightOffCommand implements ICommand {private final Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}@Overridepublic void undo() {light.turnOn();}
    }
    
  4. 创建请求者角色(这里指的就是遥控器)

    // 定义一个调用者——遥控器类,它将保存命令并执行
    public class RemoteControl {private ICommand command;public void setCommand(ICommand command) {this.command = command;}public void pressButton() {command.execute();}public void undo() {command.undo();}
    }
    

至此,我们就通过“生搬硬套”命令模式的模板设计出一套遥控器开关灯的案例,接下来我们进行测试:

  • 测试代码

    public class Client {public static void main(String[] args) {Light light = new Light();RemoteControl remote = new RemoteControl();// 设置打开电灯的命令remote.setCommand(new LightOnCommand(light));remote.pressButton(); // 执行命令:打开电灯remote.undo(); // 撤销命令:关闭电灯// 设置关闭电灯的命令remote.setCommand(new LightOffCommand(light));remote.pressButton(); // 执行命令:关闭电灯remote.undo(); // 撤销命令:打开电灯}
    }
    
  • 测试结果

    Light is on
    Light is off
    Light is off
    Light is on
    
场景描述2

假如我们开发一个播放器,播放器有播放功能、拖动进度条功能、停止播放功能、暂停功能,我们在操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传达指令给播放器内核,具体传达什么指令,会被封装为一个个按钮。那么每个按钮就相当于对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。

代码开发2
  1. 创建接收者角色(这里指的是实际执行的对象播放器)

    // 定义接收者类,即实际执行动作的对象——播放器
    public class GPlayer {public void play() {System.out.println("正常播放");}public void speed() {System.out.println("拖动进度条");}public void stop() {System.out.println("停止播放");}public void pause() {System.out.println("暂停播放");}
    }
    
  2. 创建抽象命令角色:(这里指的是抽象命令接口,定义执行命令操作)

    // 定义一个命令接口
    public interface IAction {void execute();
    }
    
  3. 创建具体命令角色(这里指的是播放、暂停、停止、加速具体命令实现类)

    // 具体的播放命令
    public class PlayAction implements IAction {private GPlayer player;public PlayAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.play();}
    }
    
    // 具体的暂停命令
    public class PauseAction implements IAction {private GPlayer player;public PauseAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.pause();}
    }
    
    // 具体的停止命令
    public class StopAction implements IAction {private GPlayer player;public StopAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.stop();}
    }
    
    // 具体的拖动进度条命令
    public class SpeedAction implements IAction {private GPlayer player;public SpeedAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.speed();}
    }
    
  4. 创建请求者角色(这里指的就是播放器控制面板)

    import java.util.ArrayList;
    import java.util.List;// 定义一个调用者——控制面板类,它将保存命令并执行
    public class Controller {private List<IAction> actions = new ArrayList<>();public void addAction(IAction action) {actions.add(action);}public void execute(IAction action) {action.execute();}public void executes() {for (IAction action : actions) {action.execute();}actions.clear();}
    }
    

至此,我们就通过“生搬硬套”命令模式的模板设计出一套通过播放器控制面板上的按钮来控制播放器的案例,接下来我们进行测试:

  • 代码测试
    public class Client {public static void main(String[] args) {GPlayer player = new GPlayer();Controller controller = new Controller();controller.execute(new PlayAction(player));controller.addAction(new PauseAction(player));controller.addAction(new PlayAction(player));controller.addAction(new StopAction(player));controller.addAction(new SpeedAction(player));controller.executes();}
    }
    
  • 测试结果
    正常播放
    暂停播放
    正常播放
    停止播放
    拖动进度条
    

总结

在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。但紧耦合关系缺乏扩展性,在某些场合中,当需要对行为进行记录、撤销或重做等处理时,只能修改源码。而命令模式通过在请求与实现间引入一个抽象命令接口,解耦了请求与实现,并且中间件是抽象的,它由不同的子类实现,因此具备扩展性。所以,命令模式的本质是解耦命令请求与处理。

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

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

相关文章

【黑马点评】0.环境配置--Redis6.2.6和可视化工具在Windows上的安装

黑马点评--0.Redis6.2.6在windows上的环境配置与可视化 0 前言1 下载安装2 解压后运行msi文件3 修改配置文件并打开Redis3.1 修改密码&#xff08;可选&#xff09;3.2 测试 4 Redis可视化&#xff08;可选&#xff09;4.1 Another Redis Desktop Manager下载安装4.2 连接Redis…

N1从安卓盒子刷成armbian

Release Armbian_noble_save_2024.10 ophub/amlogic-s9xxx-armbian (github.com) armbian下载&#xff0c;这里要选择905d adb 下载地址 https://dl.google.com/android/repository/platform-tools-latest-windows.zip 提示信息 恩山无线论坛 使用usb image tool restet a…

深入理解NumPy库:常用函数详解与数组操作指南

在数据科学和数值计算领域&#xff0c;NumPy无疑是一个强大的工具&#xff0c;它为Python提供了高效的多维数 组处理能力。无论是进行数据分析、构建机器学习模型&#xff0c;还是进行复杂的科学计算&#xff0c;NumPy都是 不可或缺的核心库之一。 numpy.array 是 NumPy 库中…

C# 获取可执行文件目录

---------------------------------------------------------------------------

SpringMVC框架:入门讲解和基础案例解析

Spring Web MVC是什么&#xff1f; Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架。使用了MVC架构模式的思想&#xff0c;将web层进行职责解耦&#xff0c;基于请求驱动指的就是使用请求-响应模型 。框架的目的就是帮助我们简化开发&…

PCB板材基本知识

术语 名称定义插图 copper foil 铜箔 Copper Clad Laminates&#xff0c;CCL 覆铜箔层压板 CCL是PCB制造的上游核心材料&#xff0c;是将电子玻纤布或其它增强材料浸以树脂&#xff0c;一面或双面覆以铜箔并经热压而制成的一种板状材料&#xff0c;担负着&#xff08;PCB&am…

优先级队列详解

一&#xff0c;优先级队列 什么是优先级队列呢&#xff0c;不知道大家了解过队列没有&#xff0c;队列是一种先进先出的数据结构&#xff0c;但是我们有时会想让优先级高的先出队列&#xff0c;所以我们出现了一种新的数据结构&#xff0c;我们实现两种主要功能得到优先级高的数…

螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习07(基于docker容器的防火墙及NAT企业实战)

7.1 网络准备 7.2 网络规划 1&#xff09;虚拟网络编辑器 点击右下方“更改设置”&#xff0c;点击“添加网络”假如vmnet3和vmnet4&#xff0c;然后分别选择vmnet3和vmnet4&#xff0c;设置为“仅主机模式”&#xff0c;按③处处理&#xff0c;去掉“使用DHCP”&#xff0c;…

ORA-19815 db_recovery_file_dest_size 100%

1、alert日志报错 ORA-19815 db_recovery_file_dest_size 100% 恢复区空间使用满 2、rm删除后操作系统空间使用&#xff0c;但V$RECOVERY_FILE_DEST记录的空间使用率仍然是满的 3、rman delete expired 归档日志后恢复正常 4、当然可以通过增大db_recovery_file_dest_size来临时…

牛客——xay loves or与 __builtin_popcount的使用

xay loves or 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行思路 题目要求我们计算有多少个正整数 yy 满足条件 x \text{ OR } y sx OR ys。这里的“OR”是指按位或运算。为了理解这个问题&#xff0c;我们需要考虑按位或运算的性质。 对于任意两个位 a_iai​ 和 b_…

HUAWEI_HCIA_实验指南_Lib1.4_配置通过Telnet登录系统

一、原理概述 Telnet(Telecommunication Network Protocol)起源于ARPANET,是最早的Internet应用之一。 Telnet 通常用在远程登录应用中&#xff0c;以便对本地或远端运行的网络设备进行配置、监控和维护。如网络中有多台设备需要配置和管理&#xff0c;用户无需为每一台设备…

NUKE 15有哪些新的改进功能?影视后期特效合成NUKE 15 安装包分享 【Mac/win】

Nuke 15是一款由英国The Foundry公司开发的专业的合成软件&#xff0c;被广泛用于电影、电视和广告制作中的后期合成和特效制作。 Nuke 15拥有强大的功能和灵活性&#xff0c;可以帮助用户处理各种复杂的合成任务&#xff0c;包括图像修复、色彩校正以及粒子特效等。它具备高效…

Java项目实战II基于Java+Spring Boot+MySQL的高校学科竞赛平台

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着高等教…

【JavaScript】JS核心语法及函数

文章目录 一、初识 JS二、JS 核心语法2-1 变量2-2 数据类型typeofString 对象 2-3 数组创建数组常用属性方法 2-4 运算符号加号运算符 减号运算符 -比较运算符逻辑运算符 2-5 控制语句for-inbreakcontinue 三、函数3-1 常用系统函数3-2 自定义函数函数声明函数调用 3-3 创建对象…

家里养有宠物应该用哪款宠物空气净化器比较好?哪款最能吸毛?

这不是国庆节刚过吗&#xff0c;我的小猫终于是平安的度过了在农村生活的时光&#xff0c;之前还担心会不会被爸妈嫌弃&#xff0c;这下好了&#xff0c;嫌弃也过了国庆节。 但是一把猫咪带回出租房&#xff0c;由于几天不在房子里待&#xff0c;猫咪对熟悉的环境又特别激动&a…

C语言贪吃蛇

#只讲逻辑不讲一些基础&#xff0c;基础大概过一遍就行# project-one: 无 (gitee.com)仓库里面有原代码 一、基础工作 1、先将你的编译器换成32位环境&#xff0c;也就是x86&#xff0c; 如果是控制台主机窗口则管&#xff0c;若不是需要改为控制台主机窗口 打开运行窗口后点…

构建宠物咖啡馆:SpringBoot框架的实现策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…

Authentication Lab | IP Based Auth Bypass

关注这个靶场的其它相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;IP Based Auth Bypass 前情提要 有些开发人员为了图方便&#xff0c;会给站点设置一个 IP 白名单&#xff0c;如果访问站点的用户的 IP 在白名单内&#xff0c;则允许访…

PDSCH(物理下行共享信道)简介

文章目录 PDSCH&#xff08;物理下行共享信道&#xff09;简介1. Transport block CRC attachment2. LDPC base graph selection3. Code block segmentation And Code Block CRC Attachment4. Channel Coding5. Rate Matching6. Code Block Concatenation7. Scrambling8. Modul…

react自定义prolayout的展开收起

关于prolayout组件&#xff1a;ProLayout高级布局&#x1f3c6; 让中后台开发更简单 包含 table form 等多个组件。https://procomponents.ant.design/components/layout // tsx文件 import ProLayout from ant-design/pro-layout; ... const [collapsed,setCollapsed]useStat…