设计模式篇---命令模式(结合spring+动态代理实现开闭)

本文主要是介绍设计模式篇---命令模式(结合spring+动态代理实现开闭),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 概念
    • 结构
    • 实例
    • 总结

概念

命令模式:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
现实生活中,我们用开关来控制一些电器的打开和关闭,比如电灯和电视。购买开关时,我们也不知道它未来用来控制哪些电器,也就是说开关和电灯、电视之间没有直接关系。安装开关可能用来控制电灯,也可能用来控制其他电器,取决于控制哪些电器的是电线,相同的开关可以通过不同的电线来控制不同的电器。
上面的例子中,开关可以理解为调用者,电线为命令类,电器为接收者。开关和电器之间并不存在耦合,想控制哪个电器,更换一根电线就可以了。

结构

在这里插入图片描述
Command(抽象命令类):其中声明了用于执行请求的execute()等方法,这些方法可以调用请求接收者的相关操作。
ConcreteCommand(具体命令类):抽象命令类的子类,它对应具体的接收者对象,将具体接收者对象绑定其中,当实现execute()方法时,将调用接收者对象的相关操作。
Invoker(调用者):它用来发送请求,关联抽象命令对象。
Receiver(接收者):接收者执行与请求相关的操作,具体实现对请求的业务处理。

实例

某系统实现一个功能,用一个按钮控制不同的功能。可以通过按钮退出系统,也可以通过按钮来显示帮助文档。
在这里插入图片描述

抽象命令类

public interface  Command {public  void execute();
}

请求发送者

public class FunctionButton {private Command command;public void setCommand(Command command) {this.command = command;}public void click() {System.out.println("点击功能键:");command.execute();}
}

退出命令类,充当具体命令类

public class ExitCommand implements Command {private SystemExitClass seObj;public ExitCommand() {seObj = new SystemExitClass();}@Overridepublic void execute() {seObj.exit();}
}

帮助命令类,充当具体命令类

public class HelpCommand implements Command {private DisplayHelpClass hcObj;public HelpCommand() {hcObj = new DisplayHelpClass();}@Overridepublic void execute() {hcObj.display();}
}

退出系统模拟实现类,充当请求接收者

public class SystemExitClass {public void exit() {System.out.println("退出系统");}
}

显示帮助文档模拟实现类,充当请求接受者

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

客户端

public class Client {public static void main(String[] args) {FunctionButton button = new FunctionButton();button.setCommand(new ExitCommand());button.click();System.out.println("------------------");button.setCommand(new HelpCommand());button.click();}
}

运行结果
在这里插入图片描述

当我们在项目开发中,更多的是结合Spring使用,当客户端调用时,具体命令类发生改变时,我们尽量不去改动客户端代码,实现的方式有很多,上一篇刚写完代理模式,这里可以结合动态代理来实现bean的切换。

简单写一个config类,不足的地方的大家可自行扩展。

@Configuration
public class BeanConfig {@Resourceprivate ExitCommand exitCommand;@Resourceprivate HelpCommand helpCommand;private SwitcherInvocationHandler switcherInvocationHandler = new SwitcherInvocationHandler();class SwitcherInvocationHandler implements InvocationHandler {private final Set<String> methodsOnObjectClass = Arrays.stream(Object.class.getMethods()).map(Method::getName).collect(Collectors.toSet());@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (methodsOnObjectClass.contains(method.getName())) {return method.invoke(this, args);}if (useExitCommand()) {return method.invoke(exitCommand, args);} else {return method.invoke(helpCommand, args);}}//需要变动的地方是这里private boolean useExitCommand() {return true;}}//通过反射创建名字为"command"的bean@Bean(name = "command")public Command getCommand() {return Reflection.newProxy(Command.class, switcherInvocationHandler);}
}

然后给各个类加上spring的注解

FunctionButton类,去掉set注入,改用Resource注解,拿取名为“command” 的bean。

@Service
public class FunctionButton {@Resourceprivate Command command;public void click() {System.out.println("点击功能键:");command.execute();}
}

ExitCommand 类

@Service
public class ExitCommand implements Command {private SystemExitClass seObj;public ExitCommand() {seObj = new SystemExitClass();}@Overridepublic void execute() {seObj.exit();}
}

HelpCommand类

@Service
public class HelpCommand implements Command {private DisplayHelpClass hcObj;public HelpCommand() {hcObj = new DisplayHelpClass();}@Overridepublic void execute() {hcObj.display();}
}

客户端直接注入发送者即可

@Service("commandClient")
public class CommandClient {@Autowiredprivate FunctionButton button;public void invoke() {button.click();}
}

写个单元测试调用一下

public class TestDemo {@Autowiredprivate CommandClient commandClient;@Testpublic void test() throws InterruptedException {commandClient.invoke();}}

调用结果:
在这里插入图片描述

总结

命令模式可以降低系统的耦合度,让请求者和接收者完全解耦,并且如果有新的命令加进来,也不用修改之前的代码,符合开闭原则。
命令模式和外观模式有些类似,都是通过中间一个对象进行解耦。命令模式更适合操作的切换,比如开关用来开灯,也可以用来开电视,开关作为调用者,可以调用开灯的命令,也可以调用开电视的命令,让调用者和接收者解耦。
外观模式更类似一组操作的集合,比如到家后,先换拖鞋,再开电视,最后坐在沙发上,这一系列的操作,可以放在外观层实现,让调用者和具体的子系统解耦。

这篇关于设计模式篇---命令模式(结合spring+动态代理实现开闭)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/573260

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

Mac系统下卸载JAVA和JDK的步骤

《Mac系统下卸载JAVA和JDK的步骤》JDK是Java语言的软件开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源,:本文主要介绍Mac系统下卸载JAVA和JDK的相关资料,需... 目录1. 卸载系统自带的 Java 版本检查当前 Java 版本通过命令卸载系统 Java2. 卸载自定

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Java Spring ApplicationEvent 代码示例解析

《JavaSpringApplicationEvent代码示例解析》本文解析了Spring事件机制,涵盖核心概念(发布-订阅/观察者模式)、代码实现(事件定义、发布、监听)及高级应用(异步处理、... 目录一、Spring 事件机制核心概念1. 事件驱动架构模型2. 核心组件二、代码示例解析1. 事件定义

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3