乐趣区

关于java:策略模式在实际业务中的应用

策略模式结构图

策略模式次要由以上三个身份组成,这里咱们就不过多及时策略模式的基础知识,默认大家曾经对策略模式曾经有了一个根底的意识。

业务需要

现有一个广告点击数据埋点上报的需要,上报的埋点数据依据点击的广告地位不同做辨别进行上报,每个广告地位的数据进行分表存储。(eg:这里大家也不用深究分表存储为什么要这么做,咱们只聊策略模式的理论利用)

代码实现

因为是实战案例,那么咱们是基于 SpringBoot 框架的,次要要应用的 Spring 的一些性能,所以大家要留神。

第一步:定义策略类

首先咱们定义一个上报的接口

public interface AdvertisingDataReported {String advertisingDataReported(Object param);
}

第二步:定义具体的策略实现类

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {
    
    @Override
    public String advertisingDataReported(Object param) {
          // 具体的业务逻辑略
        return null;
    }
}

第三步:策略管制类

因为策略模式有好多具体的具体策略实现,那么到底应用哪一个策略须要依据咱们的入参,也就是咱们业务中的广告类型进行判断,那么咱们该如何优雅的进行判断呢?

咱们先看看这种形式

public static void main(String[] args) {
        
  String advertisingType = "1";

  if (advertisingType.equals("1")) {// 执行策略 A} else if (advertisingType.equals("2")) {// 执行策略 2}
}

这么写的大有人在,咱们这里也不探讨这些问题。咱们先看一下这么写存在哪些问题?

存在的问题:

1. 违反开闭准则,每次减少新的策略实现类,都要加一个 if 判断;2. 随着策略实现类的减少,代码变的臃肿,越来越难以保护;

基于这种状况,咱们可不可以在我的项目启动的时候,将所有的策略实现类进行初始化,存储在 Map 当中,广告类型作为 key,实现类作为 Value,咱们看如下代码:

@Component
public class StrategyFactory implements ApplicationContextAware {private final Map<String, AdvertisingDataReported> STRATEGY_MAP = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
          // 返回该接口所有的实现类
        Map<String, AdvertisingDataReported> tempMap = applicationContext.getBeansOfType(AdvertisingDataReported.class);
        tempMap.values().forEach(strategyService -> STRATEGY_MAP.put(strategyService.getClass().getName(), strategyService));
    }

    public <T extends AdvertisingDataReported> AdvertisingDataReported getInstance(Class<T> clazz) {return STRATEGY_MAP.get(clazz.getName());
    }
}

咱们的策略管制类实现了 ApplicationContextAware,这个类你能够这么了解,它能够取得 ApplicationContext 的上下文,因为咱们是至于 SpringBoot 讲这个案例的,咱们的策略类实现类都加了 @Service 注解注入到了 Spring 容器中,所以咱们能够间接从容器中,取到策略类的所有实现类。

获取到所有的策略实现类之后,咱们把类门路作为 key,类的实现作为 value 存储到了 map 中,到此我过后感觉就功败垂成了。

大家感觉还存在什么问题?

咱们怎么晓得这个入参须要走哪个具体的策略类呢?还须要定义一个独自的类,来 对广告类型和策略类进行映射,那这跟判断不又是同一个逻辑的吗?还得始终保护这个映射关系。

革新

如果不想独自的定义一个类对广告类型和策略类进行一一映射,那么咱们可不可以在策略类中进行解决,每个策略类实现类晓得它要解决哪种类型,这样咱们就能够把 map 中 Key 类门路的值替换为广告类型,这样就能够依据上报接口入参的广告类型,间接从 Map 中进行 get。

具体的实现有两种,你能够自定义注解,通过加注解的形式进行辨别,也能够应用办法,那么咱们这里间接应用办法进行解决。

革新后的代码:

策略类:

public interface AdvertisingDataReported {

      // 新增办法
    AdvertisingTypeEnum advertisingType();

    String advertisingDataReported(Object param);
}

策略实现类:

@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {

    @Override
    public AdvertisingTypeEnum advertisingType() {return AdvertisingTypeEnum.BOTTOM;}

    @Override
    public String advertisingDataReported(Object param) {return null;}
}

策略管制类:

@Component
public class StrategyFactory implements ApplicationContextAware {

      // Map 的 Key 改为广告类型枚举类
    private final Map<AdvertisingTypeEnum, AdvertisingDataReported> STRATEGY_MAP = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {Map<String, AdvertisingDataReported> tempMap = applicationContext.getBeansOfType(AdvertisingDataReported.class);
        tempMap.values().forEach(strategyService -> STRATEGY_MAP.put(strategyService.advertisingType(), strategyService));
    }

      // 依据广告类型获取相应的策略类
    public <T extends AdvertisingDataReported> AdvertisingDataReported getInstance(AdvertisingTypeEnum advertisingTypeEnum) {return STRATEGY_MAP.get(advertisingTypeEnum);
    }
}

广告枚举类:

public enum AdvertisingTypeEnum {

    BOTTOM, TOP;

    private String advertisingType;

    AdvertisingTypeEnum() {}
  
      // set get 省略
}

策略类的具体应用

@RestController
public class AdvertisingDataReportedController {

    @Resource
    private StrategyFactory strategyFactory;

    @RequestMapping(value = "/reported/data", method = RequestMethod.POST)
    public String reportedData(AdvertisingTypeEnum advertisingTypeEnum, Object obj) {AdvertisingDataReported dataReported = strategyFactory.getInstance(advertisingTypeEnum);

        String result = dataReported.advertisingDataReported(obj);

        return "SUCCESS";
    }
}

小小总结:

到这里咱们这个策略模式的案例就算完结了,有几个问题不晓得大家有没有纳闷,为什么我要用 Object 作为办法的入参,咱们这种案例中,如同每个策略类的入参如同都是一样的,然而也有可能呈现同一个策略的实现类,然而入参齐全可能不雷同,那么这个时候,咱们就能够通过传入 Object 的形式,在办法外部进行转换,当然了,如果这样你嫌策略办法太死板了,那么你也能够在办法上退出泛型,具体转换为什么类型,通过调用者传入泛型来转换。

通过这样一番革新之后,方才咱们遇到的两个问题也都通通不是问题了,咱们想要新增一个策略实现类,只须要实现定义的策略类即可,无需减少额定的任何代码。

退出移动版