Java设计模式之命令模式
作者:tianClassmate 发布时间:2022-06-17 22:49:07
本文通过解决老王经常搞错借书人的问题,来引出行为型模式中的命令模式。为了在案例之上理解的更加透彻,我们需要了解命令模式在源码中的应用。最后指出命令模式的应用场景和优缺点。
读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到码云,本地源码下载。
一、引出问题
老王的书房藏书越来越多,每天来借书的人络绎不绝。每天有人借书、还书、老王将A借的书算到B头上的乌龙事件频出。老王和小王就商量着手解决这个问题。
小王提议,在老王和借书者之间再增加一个“记录员”角色,记录员只管报名字就行了,具体是借什么书由借书者自己决定就好了。
老王说:这能解决部分问题。但在真实的场景下,不可能来一个借书者“记录员”就跑一趟。而且借书者有时候会借一半临时有事就不借了。这些问题你也要考虑进去。
老王接着说:你应该,在“记录员”角色中,增加一个队列,将所有借书者都放到一个队列中,既有往队列中放命令的方法,也有从命令中移除的方法,方便“记录员”请求排队和“撤销”。
二、命令模式的概念和应用
老王提出来的正是命令模式的“白话文解释”。我们来看命令模式的官方概念:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,解耦合。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
在命令模式中有三个角色:
抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
实现者/接收者(Receiver)(老王)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
具体命令(Concrete Command)(记录员)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
我们基于概念和角色划分,实现代码:
抽象命令类:
/**
* 抽象命令类
* @author tcy
* @Date 25-08-2022
*/
public interface AbstractCommand {
//只需要定义一个统一的执行方法
void execute();
}
具体命令角色(老王):
/**
* 具体命令
* @author tcy
* @Date 25-08-2022
*/
public class ConcreteCommand implements AbstractCommand {
//持有接受者对象
private String clent;
public ConcreteCommand(String clent){
this.clent = clent;
}
@Override
public void execute() {
System.out.println("具体执行者角色(老王):"+clent+"借书...");
}
}
接收者(记录员):
/**
* 接收者
* @author tcy
* @Date 25-08-2022
*/
public class ReceiverCommand {
//可以持有很多的命令对象
private ArrayList<AbstractCommand> commands;
public ReceiverCommand() {
commands = new ArrayList();
}
public void setCommand(AbstractCommand cmd){
commands.add(cmd);
}
public void removeCommand(AbstractCommand cmd){
commands.remove(cmd);
}
// 发出命令
public void borrowBookMeaaage() {
System.out.println("接受者角色(记录员):有人来借书啦...");
//通知全部命令
for (int i = 0; i < commands.size(); i++) {
AbstractCommand cmd = commands.get(i);
if (cmd != null) {
cmd.execute();
}
}
}
}
客户端(借书者):
/**
* @author tcy
* @Date 25-08-2022
*/
public class Client {
public static void main(String[] args) {
//创建接收者
//将订单和接收者封装成命令对象
ConcreteCommand cmd1 = new ConcreteCommand( "A");
ConcreteCommand cmd2 = new ConcreteCommand( "B");
//创建具体命令者
ReceiverCommand invoker = new ReceiverCommand();
invoker.setCommand(cmd1);
invoker.setCommand(cmd2);
//喊一声有人要借书
invoker.borrowBookMeaaage();
}
}
基于命令模式实现的代码就实现了,但是看懂代码是一回事,自己能写出来就是另外一回事了。读者最好根据案例重新仿写一遍。
三、源码中的应用
在源码中使用命令模式的典型案例就是Jdk多线程章节中的Runnable ,Runnable 相当于命令模式中的抽象命令角色。Runnable 中的 run() 方法就当于 execute() 方法。
我们知道,Java中一个类实现Runnable 接口,那么该类就认为是一个线程,就相当于命令模式中的具体命令角色。
当我们调用start()方法后,就可以与别的线程强占CPU的资源,在占用CPU的线程中就会执行run()方法。CPU的调度者就相当于具体命令角色也即记录员。Runnable 就完美的实现了用户自定义线程和CPU的解耦合。
命令模式在Runnable 中的应用应该很好理解。
四、总结
优点很明显,解耦了命令请求与实现,很容易的可以增加新命令,支持命令队列。
但是,这样会不可避免的使具体命令类过多,增加了理解上的困难。
设计模式学到这种程度,我们就会发现设计模式不是一种单一的技术,而是各种技术的综合体。
我们在学习设计模式的时候一定不要仅局限于一种模式,而是站在一定的高度去整体衡量哪种设计模式才是最优的。
有时候我们会发现,使用设计模式会让我们的代码变得更加的复杂,但以自己目前的开发经验又不能确定是否采用设计模式是一个好的选择。
归根结低,还是我们对设计模式掌握的不够熟练,这就需要我们继续深入学习设计模式,当我的学完再回头看这些问题,就很自然的迎刃而解了。
来源:https://www.cnblogs.com/tianClassmate/p/16638122.html
猜你喜欢
- 在导入studio工程的时候,进行sync的时候,提示Error:Configuration with name 'default&
- java解析json数组最简单的json数组[ { &quo
- 1. 为什么要进行参数校验在后端进行工作时,需要接收前端传来的数据去数据库查询,但是如果有些数据过于离谱,我们就可以直接把它pass掉,不让
- 本文实例讲述了C++语言实现线性表之链表实现方法。分享给大家供大家参考。具体分析如下:插入、删除结点的代码有点多,但这样提高了代码的可读性,
- 前沿知识ThreadLocal存储线程变量,使用set方法设置变量,使用get方法获取变量线程隔离的实现是每个Thread类有一个类型为Th
- 一、前言最近接到一个任务,需要爬取五级行政区划的所有数据(大概71万条数据在),需要爬取的网站:行政区划 - 行政区划代码查询 发
- 1.可能是缓存导致的。解决方法:清除缓存!2.全局编译可能项目依赖别的模块,别的模块修改未进行编译,这时须先对依赖模块进行编译补充知识:ID
- 场景既然要搞懂Redis分布式锁,那肯定要有一个需要它的场景。高并发售票问题就是一个经典案例。搭建环境准备redis服务,设置redis的键
- 题目一链表题——链表合并根据给定的两个升序链表合并为一个新的升序链表具体题目如下解法/** * Definition for singly-
- 一、前言(吐槽+煽情+简介) &n
- 一、获取android工程里面的各种资源的id; 1.1 string型 比如下面: << string name=”OK”&g
- 项目中要使用到在线支付功能 目前常用的在线支付手段主要是 支付宝 和微信。 这里我使用的是支付宝支付,支付宝有个好处就是他有一个沙箱模式 即
- mybatis-plus 新增/修改 自动填充指定字段1.需要修改的字段在模型类上添加@TableField(fill = FieldFil
- 本文实例讲述了Java线程之守护线程(Daemon)用法。分享给大家供大家参考。具体如下:守护线程(Daemon)Java有两种Thread
- Java8对于LocalDateTime的序列化和反序列化这里以jackjson为例配置反序列化工具/** * 时间戳反序列化时间 * *
- 这篇文章主要介绍了SpringBoot如何读取war包jar包和Resource资源,文中通过示例代码介绍的非常详细,对大家的学习或者工作具
- 当键盘敲下后退键(Backspace)后1、禁止浏览器自动后退2、但不影响密码、单行文本、多行文本输入框等的回退操作<script t
- Spring boot项目结合docker容器用,打了个jar包,启动的时候竟然说:no main manifest attribute,
- autoMapping和autoMappingBehavior的区别autoMappingBehaviormybatis核心配置文件中set
- 对某个类型中的方法进行拦截,然后加入固定的业务逻辑,这是AOP面向切面编程可以做的事,在springboot里实现aop的方法也有很多, s