Strategy 策略模式在实际项目中的使用
引言
最近通读了《重构》这本书,其中讲到了在实际开发过程中不应该出现大量的 if…else 以及 switch…case 语句,这让我想起了自己之前做的一个项目。其中有部分代码,会使用非常多的 switch…case 语句来判断数据视图从而找到对应的处理类,而所有的处理类其实都是同一个接口的实现类。按照《重构》的指导,我决定使用策略模式(Strategy)来改造这一部分的代码。
策略模式
要实现策略模式,需要有三个要素,按《研磨设计模式》:
- Context:上下文,负责和具体的策略类交互。持有真正的策略模式。
- Strategy:抽象策略,可以是接口或抽象类,用于约束一系列具体的策略算法。
- ConcreteStrategy:策略的具体实现,即平级的各种算法的实现。
具体实现的代码就不再贴出,这些网上都很容易找到,按照步骤一步一步的实现即可。希望深入了解重构和设计模式的话可以参考《重构-改善既有代码的设计》和《研磨设计模式》这两本书。
疑问
在实际实现的过程中发现,在 Context 中,还是需要根据类型来找到对应的策略实现类,但是如果这样做的话就仅仅是分散了实现算法,并没有达到真正的消除 switch…case 语句的目的。
解决方法
经过查找资料,学习总结他人的实现方法。我发现,在 Context 中,由于各种实现类都已经被分开,我们可以使用 Map<String, ConcreteStrategy> 的方式来替代 switch … case 语句。
将需要判断的类型当做 Key,具体的实现类作为 Value,这样的话,就能够达到在 Context 中消除 switch … case 的目的了。
具体实现
如何配置这个Map,有几种方法:
- 在配置文件中配置对应的类型和实现类,如:
wechat=net.csdn.mp.WechatConcreteStratege
,再根据反射,从配置文件中获取类型对应的属性后得到对应的实例。 - 同样使用反射。可以使用“惯例优于配置”的方式来对需要判断的类型名进行约束,如定义
WechatFileUpload
、QQFileUpload
、ZhiHuFileUpload
的类型,根据规则找到对应的net.csdn.mp.WechatConcreteStratege
等实现类,再获得对应实例。 - 同样使用反射。在各个实现类上添加各类型的注解,在策略类工厂中获取所有拥有该注解的实现类的实例,将类型及对应的实例放入Map中,再根据类型来获取该工厂的Map中对应的实例。
- 使用 Spring 的 Bean。定义一个 Bean,拥有一个 Map 的 Field。在该 Bean 初始化的时候即在 Map 中配置类型及对应实现类的 Bean。
- 使用表驱动法。利用一个Dictionary集合来维护目标对象与键值之间的映射关系。
总结
策略模式的目的是降低代码间的耦合,实现开闭原则。策略模式实现起来不难,使用的场景也较多。在使用策略模式之前可以考虑下是否可以使用 Replace Type Code with Subclasses 的方式来消除条件表达式,这是一种更简单的方式。
在使用策略模式过程中出现的疑问,是属于钻进了条件表达式的死胡同里,没有站在更高的角度去考虑其实现方式。
需要阅读更多优秀的代码,学习他人的思考方式,学习不同的代码实现。