设计模式介绍
就像我们以前用手柄按键玩游戏一样,这个游戏的控制方式——按键操作就像命令模式。命令模式虽然在编程开发中用的比较少,但是这种模式在日常生活种却经常用到,例如Ctrl+C和Ctrl+v。
命令模式是把逻辑实现与操作请求分离,降低耦合,方便扩展。命令模式是行为模式中的一种,以数据驱动的方式将命令对象用构造函数的方式传递给调用者。调用者再提供相应的实现,为命令执行提供操作方法。
在命令模式的实现过程中,重要的有以下几点:
- 抽象命令类:声明执行命令的接口和方法;
- 具体的命令实现类:接口类的具体 实现可以是一组相似的行为逻辑;
- 实现者:给命令开发执行逻辑的具体实现类;
- 调用者:处理命令、实现的具体操作者,负责对外提供命令服务。
命令模式简单使用
使用场景:餐厅点餐
命令模式的核心逻辑是调用方不需要关心具体的逻辑实现。在样例中,顾客只需要把点的菜交给服务员就可以,服务员再请初始烹饪。顾客不需要与厨师交流,只需要和服务员沟通就可以。
违背设计模式实现
在不使用设计模式的情况下,用一个类就可以实现。
如果不懂设计模式直接开发,虽然可以达到目的,但对于各项菜品的扩展、厨师实现及如何调用,会变得非常耦合且难以扩展。
1 2 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语句判断类型,用于添加产品,维护这种代码需要付出大量的精力,而且实际业务的逻辑要比这复杂的多,如果都写在一个类里,会耦合的非常严重。
命令模式重构代码
命令模式可以将上述的模式拆解成三大块:命令、实现者和调用者。当有新的菜品或需要增加厨师时,就可以在指定的类架构下添加,外部的调用也会非常容易扩展。
定义命定定义(菜品接口)
1 2 3 4
| public interface ICuisine { void cook(); }
|
具体命令实现(四种菜品)
1 2 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(); } }
|
1 2 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(); } }
|
1 2 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(); } }
|
1 2 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()。命令的实现过程可以按照逻辑添加补充,这里抽象得比较简单,只是模拟一个菜品烹饪的过程,相当于点菜的同时厨师开始烹饪。
定义实现者(厨师接口)
1 2 3
| public interface ICook { void doCook(); }
|
实现者具体实现(四种厨师)
1 2 3 4 5 6
| public class GuangDongCook implements ICook { @Override public void doCook() { System.out.println("广东菜"); } }
|
1 2 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("江苏菜"); } }
|
1 2 3 4 5 6 7
| public class GuangDongCook implements ICook { @Override public void doCook() { System.out.println("广东菜"); } }
|
1 2 3 4 5 6
| public class ShanDongCook implements ICook { @Override public void doCook() { System.out.println("山东菜"); } }
|
1 2 3 4 5 6 7
| public class SiChuanCook implements ICook { @Override public void doCook() { System.out.println("四川菜"); } }
|
以上可以看到,当需要扩展厨师和菜品时,可以非常方便地添加,每一个类都具备了单一职责原则。
调用者
1 2 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语句相比,这种实现方式降低了耦合性,也方便其他命令和实现的扩展。但这种设计模式也带了一些问题,在各种命令与实现者的组合下,会扩展出很多的实现类,需要管理。