设计模式——策略模式

文章首发于个人公众号:席儒空间

前言

在平常写代码的过程中,经常会看到一长串的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中的应用

  1. 集合比较器comparator

    在集合框架中,经常需要通过构造方法传入一个比较器,或创建比较器传入如Collections的静态方法中作为方法参数,进行比较排序等。在这个架构中,Comparator就是一个抽象的策略,一个类实现了里面的compare方法,则这个类就成为具体的策略类。

  2. ThreadPoolExecutor中的四种拒绝策略

    在创建线程池时,需要传入拒绝策略,这里面也是用的策略模式。源码如下图所示

     

总结

以上就是本篇要聊的策略模式的所有内容了,其实了解之后每一种设计模式都比较简单,重要的是其中的设计思想,围绕着解耦、开闭原则等做的一系列封装。

技术文想要写好确实比较难,我也是很久才输出一篇,更多的是自己平时还是缺少系统整理输出的习惯,很多时候学的东西学了就是学了,很少记录下来,这方面还是得多多加强。

最后,各位看完觉得有收获的不妨点个赞吧~不胜感激!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值