设计模式之命令模式详解
1 概述
日常生活中,我们出去吃饭都会遇到下面的场景。我们可以将女招待理解成一个请求的发送者,用户通过它来发送一个“点餐”请求,而厨师是“点餐”请求的最终接收者和处理者,在图中,顾客和厨师之间并不存在直接耦合关系,它们通过女招待连接在一起,而不同的订单最终的餐点也不同。
在软件开发中也存在很多类似的请求发送者和接收者对象,例如一个按钮,它可能是一个“关闭窗口”请求的发送者,而按钮点击事件处理类则是该请求的接收者。为了降低系统的耦合度,将请求的发送者和接收者解耦,我们可以使用一种被称之为命令模式的设计模式来设计系统,在命令模式中,发送者与接收者之间引入了新的命令对象,将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法。
在软件开发中,我们经常需要向某些对象发送请求(调用其中的某个或某些方法),但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,此时,我们特别希望能够以一种松耦合的方式来设计软件,使得请求发送者与请求接收者能够消除彼此之间的耦合,让对象之间的调用关系更加灵活,可以灵活地指定请求接收者以及被请求的操作。
命令模式定义:
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,解耦合。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。
(注:命令模式无法解决类的个数增加的问题)
6.3.2 结构
命令模式包含以下主要角色:
- 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
- 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
6.3.3 案例实现
将上面的案例用代码实现,那我们就需要分析命令模式的角色在该案例中由谁来充当。
服务员: 就是调用者角色,由她来发起命令。
资深大厨: 就是接收者角色,真正命令执行的对象。
订单: 命令中包含订单。
类图如下:
代码如下:
public interface Command {void execute();//只需要定义一个统一的执行方法
}public class OrderCommand implements Command {//持有接受者对象private SeniorChef receiver;private Order order;public OrderCommand(SeniorChef receiver, Order order){this.receiver = receiver;this.order = order;}public void execute() {System.out.println(order.getDiningTable() + "桌的订单:");Set<String> keys = order.getFoodDic().keySet();for (String key : keys) {receiver.makeFood(order.getFoodDic().get(key),key);}try {Thread.sleep(100);//停顿一下 模拟做饭的过程} catch (InterruptedException e) {e.printStackTrace();}System.out.println(order.getDiningTable() + "桌的饭弄好了");}
}public class Order {// 餐桌号码private int diningTable;// 用来存储餐名并记录份数private Map<String, Integer> foodDic = new HashMap<String, Integer>();public int getDiningTable() {return diningTable;}public void setDiningTable(int diningTable) {this.diningTable = diningTable;}public Map<String, Integer> getFoodDic() {return foodDic;}public void setFoodDic(String name, int num) {foodDic.put(name,num);}
}// 资深大厨类 是命令的Receiver
public class SeniorChef {public void makeFood(int num,String foodName) {System.out.println(num + "份" + foodName);}
}public class Waitor {private ArrayList<Command> commands;//可以持有很多的命令对象public Waitor() {commands = new ArrayList();}public void setCommand(Command cmd){commands.add(cmd);}// 发出命令 喊 订单来了,厨师开始执行public void orderUp() {System.out.println("美女服务员:叮咚,大厨,新订单来了.......");for (int i = 0; i < commands.size(); i++) {Command cmd = commands.get(i);if (cmd != null) {cmd.execute();}}}
}public class Client {public static void main(String[] args) {//创建2个orderOrder order1 = new Order();order1.setDiningTable(1);order1.getFoodDic().put("西红柿鸡蛋面",1);order1.getFoodDic().put("小杯可乐",2);Order order2 = new Order();order2.setDiningTable(3);order2.getFoodDic().put("尖椒肉丝盖饭",1);order2.getFoodDic().put("小杯雪碧",1);//创建接收者SeniorChef receiver=new SeniorChef();//将订单和接收者封装成命令对象OrderCommand cmd1 = new OrderCommand(receiver, order1);OrderCommand cmd2 = new OrderCommand(receiver, order2);//创建调用者 waitorWaitor invoker = new Waitor();invoker.setCommand(cmd1);invoker.setCommand(cmd2);//将订单带到柜台 并向厨师喊 订单来了invoker.orderUp();}
}
6.3.4 优缺点
1,优点:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
2,缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。
- 系统结构更加复杂。
6.3.5 使用场景
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
- 系统需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
6.3.6 JDK源码解析
Runable是一个典型命令模式,Runnable担当命令的角色,Thread充当的是调用者,start方法就是其执行方法
//命令接口(抽象命令角色)
public interface Runnable {public abstract void run();
}//调用者
public class Thread implements Runnable {private Runnable target;public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}private native void start0();
}
会调用一个native方法start0(),调用系统方法,开启一个线程。而接收者是对程序员开放的,可以自己定义接收者。
/*** jdk Runnable 命令模式* TurnOffThread : 属于具体*/
public class TurnOffThread implements Runnable{private Receiver receiver;public TurnOffThread(Receiver receiver) {this.receiver = receiver;}public void run() {receiver.turnOFF();}
}
/*** 测试类*/
public class Demo {public static void main(String[] args) {Receiver receiver = new Receiver();TurnOffThread turnOffThread = new TurnOffThread(receiver);Thread thread = new Thread(turnOffThread);thread.start();}
}
设计模式之命令模式详解相关推荐
- 设计模式之命令模式详解(附应用举例实现)
文章目录 1 命令模式介绍 2 命令模式详解 2.1 命令模式结构 2.2 命令模式实现 2.3 命令模式应用举例 3 实现命令队列 1 命令模式介绍 在现实生活中人们通过使用开关来控制一些电器的打开 ...
- 设计模式之命令模式详解(故事版)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 背景:小左是魔都某公司技术部 ...
- (十二)命令模式详解(故事版)- 转
作者:zuoxiaolong8810(左潇龙),转载请注明出处. 背景:小左是魔都某公司技术部的一名屌丝程序猿,每天的工作就是维护一个20世纪的古董级项目,由于公司不大,所以公司很多制度不太完善,导致 ...
- 设计模式之模板方法模式详解
设计模式之模板方法模式详解 概述 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的 ...
- 设计模式之门面模式详解
设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...
- 设计模式——模版方法模式详解(论沉迷LOL对学生的危害)
0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...
- 设计模式之桥接模式详解
设计模式之桥接模式详解 文章目录 设计模式之桥接模式详解 一.什么是桥接模式 二.桥接模式的应用场景 三.桥接模式的角色组成 四.桥接模式通用写法示例 五.桥接模式优缺点 一.什么是桥接模式 桥接模式 ...
- 设计模式之策略模式详解
设计模式之策略模式详解 概述 先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车.可以坐汽车.可以坐火车.可以坐飞机. 作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有 ...
- 设计模式之工厂模式详解(附应用举例实现)
文章目录 1 工厂模式介绍 2 工厂模式详解 2.1 简单工厂模式 2.1.1 简单工厂模式结构 2.1.2 简单工厂模式实现 2.1.3 简单工厂模式应用举例 2.2 工厂方法模式 2.2.1 工厂 ...
最新文章
- 动态主机配置协议服务器不能提供,计算机网络基础课程—动态主机配置协议(Dhcp)...
- 控制反转(IOC)模式
- 灰度图像阈值化分割常见方法总结及VC实现
- 一年以来我最好的创意
- Splitting into digits
- 一幅图秒懂LoadAverage(转载)
- build 之前执行task_Android Gradle新增buildtypes以及编译前执行自定义task
- 数据库事务的4大特性与隔离级别
- 广船国际:“红帆”远航
- JSK-384 进制转换【入门】
- 51nod 1105 第K大的数 【双重二分/二分套二分/两数组任意乘积后第K大数】
- 完美国际服务器修改器,《完美世界国际2》155虚拟一键端 el编辑器 装备在线编辑器 GM管理后台 同步最新官方客户端Build 2567...
- 点击文本或按钮实现复制
- Nacos 学习笔记:安装运行初体验
- 清华大学计算机系毕业论文 android,清华大学计算机科学与技术系
- 区块链代采供应链金融平台方案设计手稿
- 蚂蚁区块链投票案例(二)---投票合约设计开发
- MATLAB计算排列组合
- 泛函分析和他的基础概念
- input标签能换行么?textarea标签属性
热门文章
- 渗透测试-Fastjson各版本漏洞分析(下)
- cpu风扇调速_乔思伯推出两款新风冷散热器:平价双热管和四热管风冷,带9cmRGB风扇...
- iveryone火速上车 抢第一波
- css会对网站排名有影响吗,网站是如何推广的?DIVCSS网页相似太多影响排名?
- zk session expire会引起HA模式的rm一直处于standby吗
- 如何在Apple Watch上设置和使用密码
- 【洛谷P1303 A*B Problem】
- 1-2路由器基本配置命令
- python解压版怎么安装_python 解压版 zip file 安装
- android日记本-kotlin