设计模式介绍
就像我们以前用手柄按键玩游戏一样,这个游戏的控制方式——按键操作就像命令模式。命令模式虽然在编程开发中用的比较少,但是这种模式在日常生活种却经常用到,例如Ctrl+C和Ctrl+v。
命令模式是把逻辑实现与操作请求分离,降低耦合,方便扩展。命令模式是行为模式中的一种,以数据驱动的方式将命令对象用构造函数的方式传递给调用者。调用者再提供相应的实现,为命令执行提供操作方法。
在命令模式的实现过程中,重要的有以下几点:
- 抽象命令类:声明执行命令的接口和方法;
- 具体的命令实现类:接口类的具体 实现可以是一组相似的行为逻辑;
- 实现者:给命令开发执行逻辑的具体实现类;
- 调用者:处理命令、实现的具体操作者,负责对外提供命令服务。
命令模式简单使用
使用场景:餐厅点餐
命令模式的核心逻辑是调用方不需要关心具体的逻辑实现。在样例中,顾客只需要把点的菜交给服务员就可以,服务员再请初始烹饪。顾客不需要与厨师交流,只需要和服务员沟通就可以。
违背设计模式实现
在不使用设计模式的情况下,用一个类就可以实现。
如果不懂设计模式直接开发,虽然可以达到目的,但对于各项菜品的扩展、厨师实现及如何调用,会变得非常耦合且难以扩展。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | package com.bestrookie.design;import com.alibaba.fastjson.JSON;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 
 
 
 
 
 public class XiaoEr {
 private Map<Integer, String> cuisineMap = new ConcurrentHashMap<>();
 public void  order(int cuisine){
 if (1 == cuisine){
 cuisineMap.put(1,"广东菜");
 }
 if (2 == cuisine){
 cuisineMap.put(2,"江苏菜");
 }
 if (3 == cuisine){
 cuisineMap.put(3,"山东菜");
 }
 if (4 == cuisine){
 cuisineMap.put(4,"川菜");
 }
 }
 public void placeOrder(){
 System.out.println("菜单: "+ JSON.toJSONString(cuisineMap));
 }
 }
 
 | 
可以看到有比较多的if语句判断类型,用于添加产品,维护这种代码需要付出大量的精力,而且实际业务的逻辑要比这复杂的多,如果都写在一个类里,会耦合的非常严重。
命令模式重构代码
命令模式可以将上述的模式拆解成三大块:命令、实现者和调用者。当有新的菜品或需要增加厨师时,就可以在指定的类架构下添加,外部的调用也会非常容易扩展。

定义命定定义(菜品接口)
| 12
 3
 4
 
 | public interface ICuisine {void cook();
 }
 
 
 | 
具体命令实现(四种菜品)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public class GuangDongCuisine implements ICuisine {private ICook cook;
 public GuangDongCuisine(ICook cook){
 this.cook = cook;
 }
 @Override
 public void cook() {
 cook.doCook();
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public class JiangSuCuisine implements ICuisine {private ICook cook;
 public JiangSuCuisine(ICook cook){
 this.cook = cook;
 }
 @Override
 public void cook() {
 cook.doCook();
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public class ShanDongCuisine implements ICuisine {private ICook cook;
 public ShanDongCuisine(ICook cook){
 this.cook = cook;
 }
 @Override
 public void cook() {
 cook.doCook();
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public class SiChuangCuisine implements ICuisine {private ICook cook;
 public SiChuangCuisine(ICook cook){
 this.cook = cook;
 }
 @Override
 public void cook() {
 cook.doCook();
 }
 }
 
 | 
以上是四种菜品的实现过程,在实现的类中都添加了一个厨师类(ICook),并使用这个类提供的方法操作命令(烹饪命令)cook.doCooking()。命令的实现过程可以按照逻辑添加补充,这里抽象得比较简单,只是模拟一个菜品烹饪的过程,相当于点菜的同时厨师开始烹饪。
定义实现者(厨师接口)
| 12
 3
 
 | public interface ICook {void doCook();
 }
 
 | 
实现者具体实现(四种厨师)
| 12
 3
 4
 5
 6
 
 | public class GuangDongCook implements ICook {@Override
 public void doCook() {
 System.out.println("广东菜");
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | package com.bestrookie.design.cook.impl;
 import com.bestrookie.design.cook.ICook;
 
 
 
 
 
 
 public class JiangSuCook implements ICook {
 @Override
 public void doCook() {
 System.out.println("江苏菜");
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 
 | public class GuangDongCook implements ICook {@Override
 public void doCook() {
 System.out.println("广东菜");
 }
 }
 
 
 | 
| 12
 3
 4
 5
 6
 
 | public class ShanDongCook implements ICook {@Override
 public void doCook() {
 System.out.println("山东菜");
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 
 | public class SiChuanCook implements ICook {@Override
 public void doCook() {
 System.out.println("四川菜");
 }
 }
 
 
 | 
以上可以看到,当需要扩展厨师和菜品时,可以非常方便地添加,每一个类都具备了单一职责原则。
调用者
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | public class XiaoEr {private List<ICuisine> cuisineList = new ArrayList<>();
 public void order(ICuisine cuisine){
 cuisineList.add(cuisine);
 }
 public synchronized void priceOrder(){
 for (ICuisine iCuisine : cuisineList) {
 iCuisine.cook();
 }
 cuisineList.clear();
 }
 }
 
 | 
在调用者的具体实现中,提供了菜品的添加和菜单执行烹饪任务。这个过程是命令模式的具体调用,通过外部接口调用,将菜品实现类和厨师实现类传递进来。
总结
命令模式分为命令、实现者和调用者。二这三块内容的拆分也是选择场景的因素,经过拆分,可以让逻辑具备单一职责的性质,便于扩展。与if语句相比,这种实现方式降低了耦合性,也方便其他命令和实现的扩展。但这种设计模式也带了一些问题,在各种命令与实现者的组合下,会扩展出很多的实现类,需要管理。