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


猜你喜欢
- 功能实现:1、图片加载类ImageLoader实现:1)用阻塞队列存储要图片:BlockingQueue images = new Arra
- 本文实例为大家分享了ExpandableListView二级分栏效果的具体代码,供大家参考,具体内容如下对ExpandableListVie
- 概念理解Properties 继承于 Hashtable。表示一个持久的属性集,属性列表以key-value的形式存在,key和value都
- 1.JavaBean转Map1.1.简介这篇博客是通过反射来进行实现转换的在学习redis中,发现了一个知识点,就是Java对象转map,视
- 队列的定义:队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。 (1)允许删除的一端称为队头(Fro
- 取模运算与取余运算两个概念有重叠的部分但又不完全一致。主要的区别在于对负整数进行除法运算时操作不同。对于整形数a,b来说,取模运算或者求余运
- 先看看效果图:1、XML布局引入<com.net168.lib.SortTabLayout android:id=&quo
- Java CharArrayReader流一、CharArrayReader流定义API说明:该类实现了一个可用作字符输入流的字符缓冲区,即
- IDEA安装后,前进 后退快捷按钮默认不在工具栏显示,需要手动将其添加到工具栏*按照图一选中Toolbar Run Actions ,点击右
- 之前开发项目,一直都使用的是外壳程序加子程序的模式,通过外壳程序去启动子程序,外壳程序和子程序之间的通信,是使用配置文件完成的。我总觉得这样
- 八皇后问题(N皇后问题)的回溯法求解一、问题描述在一个国际象棋棋盘上放置八个皇后,使得任何两个皇后之间不相互攻击,求出所有的布棋方法,并推广
- 很多时候,我们需要展示在客户端展示图片,而且是动态显示,即不停地自行切换图片。下面我们来看一下具体的实现方法。首先,我们需要在XML...&
- 概述从今天开始, 小白我将带大家开启 Jave 数据结构 & 算法的新篇章.栈栈 (Stack) 是一种运算受限的线性表, 遵循先进
- 使用spring框架实现数据库事务处理事务对于数据库来说是,是对sql语句的一系列操作,这些操作被组织成为一个事务。事务具有原子性的,要么全
- 1. 引入当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象, 只有通过new关键字才会产生出对象,这时系统才会
- Android自带的SeekBar是水平的,要垂直的,必须自己写一个类,继承SeekBar。一个简单的垂直SeekBar的例子:(但是它其实
- Grpc是googe开发的,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。新公司的项目服务之间的调用使用的Grpc来实现服务间
- 1. Limit实现分页1.1 为什么需要分页 减少数据的处理量1.2 使用Limit实现分页select * from user limi
- 自用项目中统一Eclipse格式化Java、JavaScript、JSP、HTML代码设置1.Window->Preferences
- 1、准备工作首先需要有一个用于旋转的图片需要考虑如何开始、结束、加速、减速2、加速减速原理本次的动画采用RotateAnimation,初始