前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >策略模式在实际业务中的应用

策略模式在实际业务中的应用

原创
作者头像
一个程序员的成长
修改2021-11-02 09:59:32
7050
修改2021-11-02 09:59:32
举报
文章被收录于专栏:bingfeng-技术bingfeng-技术

策略模式结构图

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

业务需求

现有一个广告点击数据埋点上报的需求,上报的埋点数据根据点击的广告位置不同做区分进行上报,每个广告位置的数据进行分表存储。(eg:这里大家也不必深究分表存储为什么要这么做,我们只聊策略模式的实际应用)

代码实现

由于是实战案例,那么我们是基于SpringBoot框架的,主要要使用的Spring的一些功能,所以大家要注意。

第一步:定义策略类

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

代码语言:javascript
复制
public interface AdvertisingDataReported {
​
    String advertisingDataReported(Object param);
}

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

代码语言:javascript
复制
@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {
    
    @Override
    public String advertisingDataReported(Object param) {
        // 具体的业务逻辑略
        return null;
    }
}

第三步:策略控制类

由于策略模式有好多具体的具体策略实现,那么到底使用哪一个策略需要根据我们的入参,也就是我们业务中的广告类型进行判断,那么我们该如何优雅的进行判断呢?

我们先看看这种方式

代码语言:javascript
复制
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,我们看如下代码:

代码语言:javascript
复制
@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。

具体的实现有两种,你可以自定义注解,通过加注解的方式进行区分,也可以使用方法,那么我们这里直接使用方法进行处理。

改造后的代码:

策略类:

代码语言:javascript
复制
public interface AdvertisingDataReported {
​
    // 新增方法
    AdvertisingTypeEnum advertisingType();
​
    String advertisingDataReported(Object param);
}

策略实现类:

代码语言:javascript
复制
@Service
public class BottomAdvertisingDataReported implements AdvertisingDataReported {
​
    @Override
    public AdvertisingTypeEnum advertisingType() {
        return AdvertisingTypeEnum.BOTTOM;
    }
​
    @Override
    public String advertisingDataReported(Object param) {
        return null;
    }
}

策略控制类:

代码语言:javascript
复制
@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);
    }
}

广告枚举类:

代码语言:javascript
复制
public enum AdvertisingTypeEnum {
​
    BOTTOM, TOP;
​
    private String advertisingType;
​
    AdvertisingTypeEnum() {}
  
    // set get省略
}

策略类的具体使用

代码语言:javascript
复制
@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的方式,在方法内部进行转换,当然了,如果这样你嫌策略方法太死板了,那么你也可以在方法上加入泛型,具体转换为什么类型,通过调用者传入泛型来转换。

经过这样一番改造之后,刚才我们遇到的两个问题也都统统不是问题了,我们想要新增一个策略实现类,只需要实现定义的策略类即可,无需增加额外的任何代码。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 策略模式结构图
  • 业务需求
  • 代码实现
    • 第一步:定义策略类
      • 第二步:定义具体的策略实现类
        • 第三步:策略控制类
          • 改造
            • 策略类的具体使用
            • 小小总结:
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档