命令模式(Command Pattern),给大家的第一感觉,就是给程序发送命令,比如:启动、暂停,然后程序根据接收到的命令直接执行就行。
这样的理解相对来说比较狭义,来看下命令模式官方的定义:
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
一个太狭义,一个又太晦涩。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
我对命令模式的理解是这样的:我们将请求参数,以及请求的执行逻辑、依赖对象等,封装在一个对象中,将这个对象推送到执行的引擎中,由执行引擎来驱动执行。我们通过命令模式可以更好的封装逻辑、管理逻辑。
它是面向对象的23种设计模式中的一种,属于行为模式的范围。
我们来看这样的一个例子:
工作接口类- 1 package com.example.demo.learn.pattern.behavior.command;
- 2
- 3 /**
- 4 * @discription
- 5 */
- 6 public interface IWorkCommand {
- 7
- 8 String getWorkName();
- 9
- 10 void execute();
- 11 }
复制代码 司机工作类- 1 package com.example.demo.learn.pattern.behavior.command;
- 2
- 3 import lombok.AllArgsConstructor;
- 4 import lombok.extern.slf4j.Slf4j;
- 5
- 6 /**
- 7 * @discription
- 8 */
- 9 @AllArgsConstructor
- 10 @Slf4j
- 11 public class DriverWork implements IWorkCommand {
- 12
- 13 private String name;
- 14
- 15 @Override
- 16 public String getWorkName() {
- 17 return "Driver: " + name;
- 18 }
- 19
- 20 @Override
- 21 public void execute() {
- 22 log.warn("start invoke {} work", getWorkName());
- 23 log.warn("运送货物到指定目的地");
- 24 log.warn("打扫汽车卫生");
- 25 log.warn("将汽车送回停车地点");
- 26 }
- 27 }
复制代码 程序员工作类- 1 package com.example.demo.learn.pattern.behavior.command;
- 2
- 3 import lombok.AllArgsConstructor;
- 4 import lombok.extern.slf4j.Slf4j;
- 5
- 6 /**
- 7 * @discription
- 8 */
- 9
- 10 @AllArgsConstructor
- 11 @Slf4j
- 12 public class ProgrammerWork implements IWorkCommand{
- 13
- 14 private String name;
- 15 @Override
- 16 public String getWorkName() {
- 17 return "programmer: "+name;
- 18 }
- 19
- 20 @Override
- 21 public void execute() {
- 22 log.warn("start invoke {} work", getWorkName());
- 23 log.warn("修复昨天遗留的问题.");
- 24 log.warn("完成今天的开发工作.");
- 25 log.warn("优化系统性能和稳定性.");
- 26 }
- 27 }
复制代码 执行中心- 1 package com.example.demo.learn.pattern.behavior.command;
- 2
- 3 /**
- 4 * @discription
- 5 */
- 6 public class InvokeCenter {
- 7 public void invokeWork(IWorkCommand workCommand) {
- 8
- 9 workCommand.execute();
- 10 }
- 11 }
复制代码 主类- 1 package com.example.demo.learn.pattern.behavior.command;
- 2
- 3 /**
- 4 * @discription
- 5 */
- 6 public class PatternMain {
- 7 public static void main(String[] args) {
- 8 IWorkCommand programmerWork = new ProgrammerWork("小p");
- 9 IWorkCommand driverWork = new DriverWork("小d");
- 10 InvokeCenter invokeCenter = new InvokeCenter();
- 11 invokeCenter.invokeWork(driverWork);
- 12 invokeCenter.invokeWork(programmerWork);
- 13 }
- 14 }
复制代码 输出结果是这样的:- Connected to the target VM, address: '127.0.0.1:52437', transport: 'socket'
- 15:57:07.856 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - start invoke Driver: 小d work
- 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 运送货物到指定目的地
- 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 打扫汽车卫生
- 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 将汽车送回停车地点
- 15:57:07.867 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - start invoke programmer: 小p work
- 15:57:07.867 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 修复昨天遗留的问题.
- 15:57:07.868 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 完成今天的开发工作.
- 15:57:07.868 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 优化系统性能和稳定性.
- Disconnected from the target VM, address: '127.0.0.1:52437', transport: 'socket'
- Process finished with exit code 0
复制代码 在这个例子中,我们并不直接执行各种具体的工作,而是将他们都封装到一段方法中,由执行引擎统一的来执行。
这段逻辑是不是和通过实例化Thread 的方式,进行多线程操作的逻辑很像?(请参考这篇文章)
没错,在多线程的执行中,就应用到了命令模式。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
我们结合命令模式,可以发现这种结构大概有这3个角色:
1、一个抽象的命令接口(抽象类),在这里是 ,他用来约定我们要执行的方法放在哪里,怎么执行。我们一般称之为抽象命令类 Command
2、实现了抽象命令的实现类,在这里是,我们一般通过编写这个类,来实现我们想要的逻辑。我们一般称之为具体命令类 Concrete Command
3、调用者,在这里是,我们一般通过调用者来存储和执行命令,一般也称之为 请求者/调用者 invoker
除此之外还有一个角色
实现者(Receiver),这个角色,在我们的示例中隐藏的有点深,是log对象,也就是命令对象中,真正执行逻辑操作的对象。
注意调用者invoker 是不直接持有实现者的,两者是没有耦合关系的,是通过持有命令对象,间接的持有了调用者,间接的驱动了调用者。这样做既可以让调用者不关心具体的业务(譬如说线程池从来不直接持有执行对象的引用,而只持有对应的执行方法 (run()),由执行方法来组织逻辑和解耦)。
类图大概是这样子:
有些人问,我直接依赖调用者,然后调用调用者的某些方法,来实现我需要的逻辑是否可以,
答案是可以,但是如果第三方想要执行你的这段逻辑、或者你需要将这段逻辑交给第三方去在特定的时机处理执行,你会怎么做呢?
这时候你就需要将你实现的这段逻辑封装到一个对象中,交给其他人,这时候最终又变成了命令模式的体现。
命令模式除了进行解耦,还有一个好处就是可以编排和管理业务逻辑(命令)。
举个例子:有时候我们要做的一个业务包含几件相关的事,事情之间没有先后顺序,
比如我们去超市买一瓶可乐,需要做:
1、拿可乐
2、支付
此时我们就可以将每件事各自封装成一个命令,将整个业务包含的命令,打包丢给执行引擎,这样是不是就很好处理业务了。
A业务要做:a,b,c 三个命令
B业务要做:a,c,d,e 四个命令
我们只要定义每个命令,然后封装好每块业务需要做的几件事(命令),这样面相对象的设计感觉一下子就出来了。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |