设计模式之命令模式
命令模式(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 四个命令
我们只要定义每个命令,然后封装好每块业务需要做的几件事(命令),这样面相对象的设计感觉一下子就出来了。
如果你觉得写的不错,欢迎转载和点赞。 转载时请保留作者署名jilodream/王若伊_恩赐解脱(博客链接:http://www.cnblogs.com/jilodream/