设计模式-命令模式在Java中的使用示例-桌面程序自定义功能键

场景

欲开发一个桌面版应用程序,该应用程序为用户提供了一系列自定义功能键,用户可以通过这些功能键来实现一些快捷操作。

用户可以将功能键和相应功能绑定在一起,还可以根据需要来修改功能键的设置,而且系统在未来可能还会增加一些新的功能或功能键。

如果不使用命令模式,可能这样实现。

功能键类FunctionButton充当请求的发送者,帮助文档处理类HelpHandler充当请求的接收者,在发送者FunctionButton的onClick()

方法中将调用接收者HelpHandler的display()方法。

FunctionButton:

public class FunctionButton {//帮助文档处理类,请求接收者private HelpHandler helperHandler;public void onClick(){helperHandler = new HelpHandler();//显示帮助文档helperHandler.display();}
}

HelpHandler:

public class HelpHandler {public void display(){System.out.println("显示帮助文档");}
}

以上存在的问题:

(1) 由于请求发送者和请求接收者之间存在方法的直接调用,耦合度很高,更换请求接收者必须修改发送者的源代码,

如果需要将请求接收者HelpHandler改为WindowHanlder(窗口处理类),则需要修改FunctionButton的源代码,违背了“开闭原则”。

(2) FunctionButton类在设计和实现时功能已被固定,如果增加一个新的请求接收者,如果不修改原有的FunctionButton类,

则必须增加一个新的与FunctionButton功能类似的类,这将导致系统中类的个数急剧增加。

由于请求接收者HelpHandler、WindowHanlder等类之间可能不存在任何关系,它们没有共同的抽象层,

因此也很难依据“依赖倒转原则”来设计FunctionButton。

(3) 用户无法按照自己的需要来设置某个功能键的功能,一个功能键类的功能一旦固定,在不修改源代码的情况下无法更换其功能,

系统缺乏灵活性。

命令模式概述

在软件开发中,我们经常需要向某些对象发送请求(调用其中的某个或某些方法),但是并不知道请求的接收者是谁,

也不知道被请求的操作是哪个,此时,我们特别希望能够以一种松耦合的方式来设计软件,使得请求发送者与请求接收者

能够消除彼此之间的耦合,让对象之间的调用关系更加灵活,可以灵活地指定请求接收者以及被请求的操作。

命令模式为此类问题提供了一个较为完美的解决方案。

命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,

而不必知道如何完成请求。

命令模式(Command Pattern):

将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

命令模式结构图

命令模式包含的各角色:

Command(抽象命令类):

抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,

通过这些方法可以调用请求接收者的相关操作。

ConcreteCommand(具体命令类):

具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,

将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。

Invoker(调用者):

调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,

因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,

再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。

Receiver(接收者):

接收者执行与请求相关的操作,它具体实现对请求的业务处理。命令模式的本质是对请求进行封装,

一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:

请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。

命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,

更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。

注:

博客:
霸道流氓气质_C#,架构之路,SpringBoot-CSDN博客

实现

使用命令模式实现以上用户自定义功能键。

1、新建抽象命令类

//抽象命令类
abstract class Command {public abstract void execute();
}

2、新建具体命令类:帮助命令类

//帮助命令类:具体命令类
public class HelpCommand extends Command{//维持对请求接收者的引用private HelpHandler helpHandler;public HelpCommand(){helpHandler = new HelpHandler();}//命令执行方法,将调用请求接收者的业务方法public void execute() {helpHandler.display();}
}

其中维持着对请求接收者的引用。

3、新建请求接收者,帮助文档的处理类

//帮助文档处理类:请求接受者
public class HelpHandler {public void display(){System.out.println("显示帮助文档");}
}

4、同理新建具体命令类:最小化命令类

//最小化命令类:具体命令类
public class MinimizeCommand extends Command{//维持对请求接收者的引用private WindowHandler windowHandler;public MinimizeCommand(){windowHandler = new WindowHandler();}//命令执行方法,将调用请求接收者的业务方法public void execute() {windowHandler.minimize();}
}

其中维持着对请求接收者最小化窗口处理类的引用

5、新建最小化窗口处理类

//窗口处理类:请求接收者
public class WindowHandler {public void minimize(){System.out.println("将窗口最小化");}
}

6、新建请求发送者:功能键类

//功能键类:请求发送者
public class FunctionButton {//功能键名称private String name;//维持一个抽象命令对象的引用private Command command;public FunctionButton(String name) {this.name = name;}public String getName(){return this.name;}//为功能键注入命令public void setCommand(Command command){this.command = command;}public void onClick(){System.out.println("点击功能键:");command.execute();}
}

7、新建功能键设置窗口类

import java.util.ArrayList;//功能键设置窗口类
public class FBSettingWindow {//窗口标题private String title;//定义一个ArrayList来存储所有功能键private ArrayList<FunctionButton> functionButtons = new ArrayList<FunctionButton>();public FBSettingWindow(String title) {this.title = title;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public void addFunctionButton(FunctionButton fb){functionButtons.add(fb);}public void removeFunctionButton(FunctionButton fb){functionButtons.remove(fb);}//显示窗口及功能键public void display(){System.out.println("显示窗口:"+this.title);System.out.println("显示功能键:");for (Object obj : functionButtons) {System.out.println(((FunctionButton)obj).getName());}System.out.println("-------------------------");}
}

8、客户端调用方式

public class Client {public static void main(String[] args) {FBSettingWindow fbsw = new FBSettingWindow("功能键设置");FunctionButton fb1,fb2;fb1 = new FunctionButton("功能键1");fb2 = new FunctionButton("功能键2");Command command1,command2;//通过读取配置文件或其它方式生成具体命令对象command1 = new HelpCommand();command2 = new MinimizeCommand();//将命令对象注入功能键fb1.setCommand(command1);fb2.setCommand(command2);fbsw.addFunctionButton(fb1);fbsw.addFunctionButton(fb2);fbsw.display();//调用功能键的业务方法fb1.onClick();fb2.onClick();}
}

9、总结

如果需要修改功能键的功能,例如某个功能键可以实现“自动截屏”,只需要对应增加一个新的具体命令类,

在该命令类与屏幕处理者(ScreenHandler)之间创建一个关联关系,然后将该具体命令类的对象通过配置文件注入到某个功能键即可,

原有代码无须修改,符合“开闭原则”。在此过程中,每一个具体命令类对应一个请求的处理者(接收者),

通过向请求发送者注入不同的具体命令对象可以使得相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,

用不同的请求对客户进行参数化”,客户端只需要将具体命令对象作为参数注入请求发送者,无须直接操作请求的接收者。

命令模式的主要优点:

(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,

相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。

(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,

无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。

(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。

(4) 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案。

命令模式的主要缺点如下:

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,

因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用场景

在以下情况下可以考虑使用命令模式:

(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,

接收者也无须关心何时被调用。

(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,

换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,

而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。

(3) 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

(4) 系统需要将一组操作组合在一起形成宏命令。

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

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

相关文章

使用 OpenCV 进行图像模糊度检测(拉普拉斯方差方法)

写在前面 工作中遇到&#xff0c;简单整理人脸识别中&#xff0c;对于模糊程度较高的图像数据&#xff0c;识别率低&#xff0c;错误率高。虽然使用 AdaFace 模型&#xff0c;对低质量人脸表现尤为突出。但是还是需要对 模糊程度高的图像进行丢弃处理当前通过阈值分类&#xff…

临时段的cleanup引起的enq:TT–content等待事件处理过程

文章目录 1.问题描叙2.查因过程3.根因4.处理过程4.1 Mark目标segment为CORRUPT4.2 Drop目标segment4.3 释放占用的空间 1.问题描叙 接到用户抱怨无法修改表架构&#xff1a; 2.查因过程 查看当前DB活动&#xff1a; Select sid,serial#,osuser,program,terminal,sql_id,bl…

计算机视觉:图像质量评价指标之 PSNR 和 SSIM

1. PSNR (Peak Signal-to-Noise Ratio) 峰值信噪比 由上可见&#xff0c;PSNR相对MSE多了一个峰值&#xff0c;MSE是绝对误差&#xff0c;再加上峰值是一个相对误差指标 一般地&#xff0c;针对 uint8 数据&#xff0c;最大像素值为 255,&#xff1b;针对浮点型数据&#xff…

基于注解手写Spring的IOC(上)

一、思路 先要从当前类出发找到对应包下的所有类文件&#xff0c;再从这些类中筛选出类上有MyComponent注解的类&#xff1b;把它们都装入Map中&#xff0c;同时类属性完成MyValue的赋值操作。 二、具体实现 测试类结构&#xff1a; 测试类&#xff1a;myse、mycontor、BigSt…

2023牛客暑期多校-J-Qu‘est-ce Que C‘est?(DP)

题意&#xff1a; 给定长度为n的数列,要求每个数都在的范围&#xff0c;且任意长度大于等于2的区间和都大于等于0&#xff0c;问方案数。。 思路&#xff1a; 首先要看出是dp题&#xff0c;用来表示遍历到第i位且后缀和最小为x的可行方案数&#xff08;此时的后缀可以只有最…

IT技术面试必备:如何做好IT类技术面试?

博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试题大全》 &#x1f369;惟余辈才疏学浅&#xff0c;临摹之作或有不妥之处&#xff0c;还请读者海涵指正。☕&#x1f36d; 《MYSQL从入门到精通》数据库是开发者必会基础之…

【Linux】多线程的补充

1 线程安全的单例模式 1.1 什么是单例模式 单例模式是一种 "经典的, 常用的, 常考的" 设计模式. 1.2 什么是设计模式 IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些…

04-导数判断凹(concave)凸(convex)性_导数用于泰勒展开

导数与函数凹凸性的关系 函数的二阶导数是和函数的凹凸性是有关系的&#xff0c;凹凸性怎么定义的&#xff1f; 先来做简单的回顾&#xff0c;更多的会在最优化方法里面给大家讲&#xff0c;这里先记住凸函数是向下凸的&#xff0c; 反正就是凹的&#xff0c;是否是凸函数可以…

秒级体验本地调试远程 k8s 中的服务

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 背景 在这个以k8s为云os的时代&#xff0c;程序员在日常的开发过程中&#xff0c;肯定会遇到各种问题&#xff0c;比如&#xff1a;本地开发完&#xff0c;需要部署到远程k8s集群&#xff0c;本地…

【设计模式】详解观察者模式

文章目录 1、简介2、观察者模式简单实现抽象主题&#xff08;Subject&#xff09;具体主题&#xff08;ConcreteSubject&#xff09;抽象观察者&#xff08;Observer&#xff09;具体观察者&#xff08;ConcrereObserver&#xff09;测试&#xff1a; 观察者设计模式优缺点观察…

十八章:用于弱监督语义分割的自监督等变注意力机制

0.摘要 图像级弱监督语义分割是一个具有挑战性的问题&#xff0c;近年来得到了深入研究。大多数先进的解决方案利用类激活图&#xff08;CAM&#xff09;。然而&#xff0c;由于全监督和弱监督之间存在差距&#xff0c;CAM几乎无法用作对象掩码。在本文中&#xff0c;我们提出了…

[JAVAee]定时器

目录 定时器的含义 定时器的使用 定时器的解析 ①TaskQueue ​②TimerThread ③Timer 定时器的模拟实现 ①创建Task自定义类型 ②创建TimerThread类 ③Timer类 完整代码 定时器的含义 从名字上看,就是我们通俗理解的那个定时器.设置一定的时间,并在一定的时间后发生…

安卓抓包神奇黄鸟HttpCanary安装配置及使用教程

1、下载安装包 黄鸟抓包下载地址 2、安装下载的apk 3、证书安装问题 vivo手机我安装时打开黄鸟app&#xff0c;会直接弹出&#xff0c;直接安装即可 其他手机&#xff0c;需要去系统设置中安装 3.1 搜索 证书&#xff0c;选择CA证书 3.2 进行本人操作验证 3.3 安装HttpCa…

黄东旭:The Future of Database,掀开 TiDB Serverless 的引擎盖

在 PingCAP 用户峰会 2023 上&#xff0c; PingCAP 联合创始人兼 CTO 黄东旭 分享了“The Future of Database”为主题的演讲&#xff0c; 介绍了 TiDB Serverless 作为未来一代数据库的核心设计理念。黄东旭 通过分享个人经历和示例&#xff0c;强调了数据库的服务化而非服务化…

C语言假期作业 DAY 01

题目 1.选择题 1、执行下面程序&#xff0c;正确的输出是&#xff08; &#xff09; int x5,y7; void swap() { int z; zx; xy; yz; } int main() { int x3,y8; swap(); printf("%d,%d\n"&#xff0c;x, y)…

Android Studio 代码模板插件实现

Android Studio 代码模板插件 背景 可以跳过背景和简述&#xff0c;从模板插件实现开始看. 开发新页面时&#xff0c;原先需要写一堆模板代码。比如用Databinding写列表结构的页面&#xff0c;需要手写以下文件&#xff1a; XxActivity.ktXxFragment.ktXxViewModel.ktXxListA…

基于K8s环境·使用ArgoCD部署Jenkins和静态Agent节点

今天是「DevOps云学堂」与你共同进步的第 47天 第⑦期DevOps实战训练营 7月15日已开营 实践环境升级基于K8s和ArgoCD 本文节选自第⑦期DevOps训练营 &#xff0c; 对于训练营的同学实践此文档依赖于基础环境配置文档&#xff0c; 运行K8s集群并配置NFS存储。实际上只要有个K8s集…

CAD .NET 15.0 企业版 Crack

CAD .NET 15.0 企业版 企业版 企业版 企业版 企业版 Updated: June 14, 2023 | Version 15.0 NEW CAD .NET is a library for developing solutions in .NET environment. It supports AutoCAD DWG/ DXF, PLT and other CAD formats. The library can be used in a wide rang…

C语言之pthread_cond_t信号变化探究总结(八十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【RabbitMQ(day2)】默认(直连)交换机的应用

文章目录 一、第一种模型&#xff08;Hello World&#xff09;二、第二种模型&#xff08;work queue&#xff09;自动确认机制的后果和公平分配 三、阐述默认交换机 这篇博客是以下资料学后的总结&#xff1a; 不良人的RabbitMQ的教学视频 官方启动教程 RabbitMQ中文文档 一、…