命令模式(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 四个命令
我们只要定义每个命令,然后封装好每块业务需要做的几件事(命令),这样面相对象的设计感觉一下子就出来了。