当前位置: 首页 > news >正文

常用设计模式系列(十七)—命令模式

常用设计模式系列(十七)—命令模式

第一节、前言

各位老铁好!
今天我来跟大家分享对象行为型模式第二章节——《命令模式》,“命令”一词,通俗易懂,我们在生活中经常会发出各种各样的命令,就像你告诉你手机上的“siri”、“小爱同学”打开音乐、切歌等等命令,而你的“语音小助理”根据不同的指令完成对不同软件的不同操作,这个过程使用的就是“命令模式”。

第二节、命令模式

命令模式概念:

命令模式(Command Pattern):
将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者或者记录请求日志以及支持可撤销的操作

命令模式的构成:

在这里插入图片描述

个人理解

将用户的请求即命令进行抽象,抽象为一个对象,每个客户端调用不同的对象即发起不同的命令来完成指令,命令模式需要支持当请求较多时候的请求队列排队,并且记录请求过程的日志,并且要支持可撤销的动作。

场景举例:

在日常生活中,我们使用开关来控制一些电器的打开或者关闭,例如常见的电灯、排风扇、门禁等等,这个开关可以安装到不同的电器上,那这样对应安装的开关就能控制对应的电器,所以开关与电器没有直接的关系,因为买来的开关可能控制灯泡,可能控制其它电器开关。但是开关与电器之间使用电线连接,开关打开时,电线通电,电器正常工作,相反则电器停止工作,相同的开关可以通过不同的线路来控制不同的电器。这个时候发送者(开关)换个接受者(电器)就可以解耦,发送的对象(开关)只需要知道如何发送请求,而不需要知道如何完成请求(完成请求是电线的职责),这个解耦后运转的模式,称为命令模式。

结构图:

image

第三节、场景分析及代码实现

场景分析

我们在使用电脑操控的时候,使用的是鼠标去点击系统的某个按钮,按钮上的字展示的不同的功能,然后点击按钮,按钮会根据不同的指令完成某个功能,例如某个系统有主页按钮,登录按钮、退出按钮;

可以分析有如下几个角色

1.功能按钮当做调用者。
2.Command类当做抽象命令。
3.可设计三个具体命令类:主页命令类、登录命令类、退出命令类。
4.可以设计出一个系统类用来当做接收者,包含访问主页命令、登录命令、退出命令

UML图

image

代码实现

1.调用者功能按钮类FunctionButton

package com.yang.command;/*** @ClassName FunctionButton* @Description 功能按钮类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class FunctionButton {/*** 命令*/private Command command;public FunctionButton(Command command){this.command = command;}/*** 点击动作*/public void click(){//执行命令command.execute();}
}

2.抽象命令类Command

package com.yang.command;/*** @ClassName Command* @Description 抽象命令类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public interface Command {/*** 执行命令*/public void execute();
}

3.访问主页命令IndexCommand

package com.yang.command;/*** @ClassName IndexCommand* @Description 访问主页命令类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class IndexCommand implements Command{/*** 系统类*/private SystemMenu systemMenu;public IndexCommand(){systemMenu = new SystemMenu();}@Overridepublic void execute() {systemMenu.toIndex();}
}

4.登录命令类LoginCommand

package com.yang.command;/*** @ClassName LoginCommand* @Description 登录命令类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class LoginCommand implements Command{/*** 系统类*/private SystemMenu systemMenu;public LoginCommand(){systemMenu = new SystemMenu();}@Overridepublic void execute() {systemMenu.toLogin();}
}

5.退出命令类LogoutCommand

package com.yang.command;/*** @ClassName LogOutCommand* @Description 退出命令类* @Author IT小白架构师之路* @Date 2021/1/25 14:51* @Version 1.0**/
public class LogOutCommand implements Command{/*** 系统类*/private SystemMenu systemMenu;public LogOutCommand(){systemMenu = new SystemMenu();}@Overridepublic void execute() {systemMenu.toLogOut();}
}

6.系统类作为调用者SystemMenu

package com.yang.command;/*** @ClassName SystemMenu* @Description 系统菜单类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class SystemMenu {/*** 访问主页*/public void toIndex(){System.out.println("当前是主页");}/*** 登录动作*/public void toLogin(){System.out.println("登录成功");}/*** 退出动作*/public void toLogOut(){System.out.println("您已退出");}
}

7.创建客户端进行测试

package com.yang.command;/*** @ClassName Client* @Description 客户端* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class Client {public static void main(String[] args) {//要发起访问主页命令Command command = new IndexCommand();//功能按钮FunctionButton functionButton = new FunctionButton(command);//点击functionButton.click();System.out.println("-------------------我是分割线-----------------------");//要发起登录命令command = new LoginCommand();//功能按钮functionButton = new FunctionButton(command);//点击functionButton.click();System.out.println("-------------------我是分割线-----------------------");//要发起登出命令command = new LogOutCommand();//功能按钮functionButton = new FunctionButton(command);//点击functionButton.click();System.out.println("-------------------我是分割线-----------------------");}
}

8.测试结果如下

当前是主页
-------------------我是分割线-----------------------
登录成功
-------------------我是分割线-----------------------
您已退出
-------------------我是分割线-----------------------

使用代码实现队列

加队列的目的是,当业务发送方为多个、业务接收方为多个时,可以使用队列存储多个命令对象,不同的命令对象可以对应不同的请求者。增加队列后,可以将命令存放在队列中,一次执行,类似批量执行。

1.创建命令队列类

package com.yang.command;import java.util.ArrayList;/*** @ClassName CommandQueue* @Description 命令队列* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class CommandQueue {//队列private ArrayList<Command> commands = new ArrayList<Command>();//添加命令public void addCommand(Command command){commands.add(command);}//删除命令public void delCommand(Command command){commands.remove(command);}//执行public void execute(){for (Command command : commands){command.execute();}}
}

2.改造调用者为队列版FunctionButtonQueue

package com.yang.command;/*** @ClassName FunctionButtonQueue* @Description 调用者队列版* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class FunctionButtonQueue {//队列对象private CommandQueue commandQueue;public FunctionButtonQueue(CommandQueue  commandQueue){this.commandQueue = commandQueue;}//处理public void click(){commandQueue.execute();}
}

3.客户端测试

package com.yang.command;/*** @ClassName ClientQueue* @Description 队列版测试* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class ClientQueue {public static void main(String[] args) {CommandQueue commandQueue = new CommandQueue();//要发起访问主页命令Command command = new IndexCommand();commandQueue.addCommand(command);//要发起登录命令command = new LoginCommand();commandQueue.addCommand(command);//要发起登出命令command = new LogOutCommand();commandQueue.addCommand(command);//执行FunctionButtonQueue functionButtonQueue = new FunctionButtonQueue(commandQueue);functionButtonQueue.click();}
}

4.执行结果

当前是主页
登录成功
您已退出

增加命令缓存来完成撤销

1.增加临时缓存类CommandCache

package com.yang.command;/*** @ClassName CommandCache* @Description 执行缓存* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class CommandCache {private static Command lastCommad;private static Command currentCommand;public static Command getLastCommad() {return lastCommad;}public static void setLastCommad(Command lastCommad) {CommandCache.lastCommad = lastCommad;}public static Command getCurrentCommand() {return currentCommand;}public static void setCurrentCommand(Command currentCommand) {CommandCache.currentCommand = currentCommand;}
}

2.优化功能按钮类

package com.yang.command;/*** @ClassName FunctionButton* @Description 功能按钮类* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class FunctionButton {/*** 命令*/private Command command;public FunctionButton(Command command){this.command = command;}/*** 点击动作*/public void click(){Command lastCommad = CommandCache.getLastCommad();Command currentCommand = CommandCache.getCurrentCommand();//都为空则为本次,第一次撤销无意义if(null == lastCommad && null == currentCommand){CommandCache.setCurrentCommand(command);CommandCache.setLastCommad(command);}else{//你不为空则把上次的最后一次进行执行CommandCache.setLastCommad(currentCommand);CommandCache.setCurrentCommand(command);}//执行命令command.execute();}public void revert(){System.out.println("--------撤销开始------");Command lastCommad = CommandCache.getLastCommad();lastCommad.execute();System.out.println("--------撤销结束------");}
}

3.测试撤销Client

package com.yang.command;/*** @ClassName RevertClient* @Description 注释* @Author IT小白架构师之路* @Date 2021/1/25* @Version 1.0**/
public class RevertClient {public static void main(String[] args) {//要发起访问主页命令Command command = new IndexCommand();//功能按钮FunctionButton functionButton = new FunctionButton(command);//点击functionButton.click();System.out.println("-------------------我是分割线-----------------------");functionButton.revert();//要发起登录命令command = new LoginCommand();//功能按钮functionButton = new FunctionButton(command);//点击functionButton.click();System.out.println("-------------------我是分割线-----------------------");//撤销functionButton.revert();}
}

4.测试结果,完成了撤销操作,回到了上一个界面

当前是主页
-------------------我是分割线-----------------------
--------撤销开始------
当前是主页
--------撤销结束------
登录成功
-------------------我是分割线-----------------------
--------撤销开始------
当前是主页
--------撤销结束------

第四节

优缺点及适用场景

优点

1.可以降低系统的耦合度,由于请求者和接收者之间不存在直接调用,则请求者与接收者之间做到了完全解耦,相同的请求者也可以做到调用不同的接受者,同样接收者可以给不同的请求者使用,互相独立,故降低了耦合度。

2.新的命令可以更快的增加到系统中,创建新的具体命令类不会影响其他类

3.可以简单的设计出一个队列命令进行批量执行

4.为撤销和恢复提供了一种设计和实现方案

缺点

1.当命令逐渐增加时,系统会增加很多的具体命令类。

2.当命令增多时,开发人员维护成本变高

适用场景

1.系统需要将调用者与接受者进行解耦,不需要两者进行交互的场景下。

2.系统要在不同的时间指定请求,将请求进行排队或者批量执行是。

3.系统需要增加通用的撤销、恢复功能时。

4.系统需要将一组操作指令进行抽象,完成通用设计时。

扫描二维码

关注我吧

IT小白架构师之路

image

http://www.lryc.cn/news/605645.html

相关文章:

  • LCM中间件入门(2):LCM核心实现原理解析
  • 《人工智能导论》(python版)第2章 python基础2.2编程基础
  • [算法]Leetcode3487
  • Video_1920×1080i 1920_1080p
  • 大白话解释---FreeRTOS中的队列集
  • 基于知识驱动的解释性条件扩散模型用于无对比剂心肌梗死增强合成|文献速递-医学影像算法文献分享
  • CSS和XPATH选择器对比
  • 《Java 程序设计》第 15 章 - 事件处理与常用控件
  • Vibe Coding:AI驱动开发的安全暗礁与防护体系
  • 异步I/O和同步I/O
  • Day15--二叉树--222. 完全二叉树的节点个数,110. 平衡二叉树,257. 二叉树的所有路径,404. 左叶子之和
  • 在Linux中创建LVGL应用
  • Kotlin -> 普通Lambda vs 挂起Lambda
  • 【Django】-1- 开发项目搭建
  • Django模型迁移指南:从命令用法到最佳实践
  • HttpServletRequest详细解释
  • 如何在NPM上发布自己的React组件(包)
  • 人工智能概念之十一:常见的激活函数与参数初始化
  • Cesium 快速入门(四)相机控制完全指南
  • langchain--1--prompt、output格式、LCEL示例
  • 【烧脑算法】Dijkstra 算法:解决最短路问题
  • 会议室预定系统核心技术:如何用一行SQL解决时间冲突检测难题
  • LLC电源原边MOS管DS增加RC吸收对ZVS的影响分析
  • ode with me是idea中用来干嘛的插件
  • 山东移动云主机:技术架构与特性解析
  • AI 安监系统:为工业园安全保驾护航
  • 1 机器学习概述 (第一天2025.7.31)
  • RabbitMQ 队列配置设置 RabbitMQ 消息监听器的并发消费者数量java
  • Java 大视界 -- Java 大数据在智能医疗远程健康监测与疾病预防预警中的应用(374)
  • Linux 进程管理与计划任务