文章首发于个人公众号:席儒空间
前言
在平常写代码的过程中,经常会看到一长串的if else判断,这让代码看起来很繁琐,且不易修改,稍微改错了就容易引起大的错误。设计模式中有专门的几类模式就是用来解决这种问题,其中策略模式就是其中一种,最近正好学习了策略模式并把它用到了项目中,因此,本篇就来聊聊策略模式的实现及其应用。

定义
先来看看策略模式的定义
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式用于解耦策略的定义、创建、使用这三部分。
以上定义摘自《设计模式》一书
上面的定义还是不好理解,别急,先看下该模式的类图长啥样

对应上图结合定义,剖析下一个完整的策略模式包含的三部分
- 定义,包含一个策略接口和一组实现这个接口的策略类,对应上图右侧的接口及具体的实现。
- 创建,一般使用工厂类进行策略的创建,封装了策略创建的细节,对应上图左侧的 context 类。
- 使用,在使用策略时一般有两种确定方法,一是编译时静态确定,二是在运行时动态确定,第二种方式较为常用。
那这个代码是怎么实现的呢?
简单实现
举个例子,比如我们即将到来的端午假期,很多人会选择出门浪,大家的交通方式也都所不同,有人选择乘飞机、有人选择坐高铁,有人选择坐轮渡等等,以这个场景为例。
-
首先定义我们的抽象策略
public interface TravelStrategy{ void algorithm(); } -
其次定义我们的实现类实现上面的接口
public class AirPlanelStrategy implements TravelStrategy{ public void algorithm(){ System.out.println("乘飞机去浪~") } }public class TrainStrategy implements TravelStrategy{ public void algorithm(){ System.out.println("乘火车去浪~") } }public class BoatStrategy implements TravelStrategy{ public void algorithm(){ System.out.println("乘船去浪~") } } -
最后,创建策略
public class StrategyContext { // 使用map缓存所有策略 private static final Map<String, TravelStrategy> strategies = new HashMap<>(); static { strategies.put("train", new TrainStrategy()); strategies.put("boat", new BoatStrategy()); strategies.put("airPlane", new AirPlanelStrategy()); } public static TravelStrategy getStrategy(String travelType) { return strategies.get(travelType); } public static void main(String[] args) { TravelStrategy trainStrategy = StrategyContext.getStrategy("train"); trainStrategy.algorithm(); // 输出:乘火车去浪~ } }
结合上述代码,相信大家对这个模式应该有了一定的理解。下面我结合自己在项目中的实际应用再加深下
项目中的应用
背景
在我们的系统中有两张功能相似的表,两张表的大部分字段数据都是相同的,只有几个字段不同(PS: 我也不知道这样的设计是基于一个什么样的初衷:( )。为了不透露信息,暂且称这两张表是A表和B表吧。
现在的场景是需要针对A表和B表做相同的操作,比如操作1对应的是A表的查询,操作2对应的是B表的查询, 由于这两张表的数据大致上都是相同的,所以查询逻辑也差不多。
针对这么个场景,我第一时间想到的就是if else。伪代码如下
public List<T> select(String type) {
if (type.equals("1")) {
return mapperA.select();
}
if (type.equals("2")) {
return mapperB.select();
}
}
在起初这样的方式没什么问题,后面慢慢地就凸显出问题了,因为需求一调整,就需要更多的if else逻辑判断,于是我采用了策略模式对其进行了重构。
-
定义了一个抽象类,在抽象类中定义了一个抽象方法(这里我使用的是抽象类,用接口也是一样的)
@Component public abstract class AbstractStrategy { abstract List<T> select(); } -
分别定义两个具体的类,继承上面的抽象类
@Service("concreteStrategyA") public class ConcreteStrategyA extends AbstractStrategy { @Autowired private MapperA mapperA; @Override public List<A> select() { return mapperA.select(); } }@Service("concreteStrategyB") public class ConcreteStrategyB extends AbstractStrategy { @Autowired private MapperB mapperB; @Override public List<B> select() { return mapperB.select(); } } -
定义一个工厂类,用于创建策略
@Service public class Context { @Autowired @Qualifier("concreteStrategyA") private AbstractStrategy concreteStrategyA; @Autowired @Qualifier("concreteStrategyB") private AbstractStrategy concreteStrategyB; private static final Map<String, AbstractStrategy> strategies = new HashMap<>(); static { strategies.put("A", concreteStrategyA); strategies.put("B", concreteStrategyB); } public static AbstractStrategy get(String s) { return strategies.get(s); } } -
最后,原先的查询逻辑就可改为下面这种了
// 注入工厂类 @Autowired private Context context; // 改造原先查询方法 public List<T> select(String type) { return context.get(type).select(); }
通过上述方法的改造,我不用再写很多if else 的判断逻辑了,只需要在各自的具体类里去实现即可,很大程度上进行了解耦,也更加方便维护。
在JDK中的应用
-
集合比较器comparator
在集合框架中,经常需要通过构造方法传入一个比较器,或创建比较器传入如Collections的静态方法中作为方法参数,进行比较排序等。在这个架构中,Comparator就是一个抽象的策略,一个类实现了里面的compare方法,则这个类就成为具体的策略类。
-
ThreadPoolExecutor中的四种拒绝策略
在创建线程池时,需要传入拒绝策略,这里面也是用的策略模式。源码如下图所示

总结
以上就是本篇要聊的策略模式的所有内容了,其实了解之后每一种设计模式都比较简单,重要的是其中的设计思想,围绕着解耦、开闭原则等做的一系列封装。
技术文想要写好确实比较难,我也是很久才输出一篇,更多的是自己平时还是缺少系统整理输出的习惯,很多时候学的东西学了就是学了,很少记录下来,这方面还是得多多加强。
最后,各位看完觉得有收获的不妨点个赞吧~不胜感激!
2万+

被折叠的 条评论
为什么被折叠?



