当前位置: 首页 > 后端技术 > Java

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

时间:2023-04-02 01:43:47 Java

策略模式结构图策略模式主要由以上三个恒等式组成,这里就不过多介绍策略模式的基础知识了,默认大家已经基本了解了策略模式。业务需求需要上报广告点击数据埋点。上报的埋点数据是根据点击广告的位置上报的,每个广告位置的数据存储在一个单独的表中。(eg:这里不用深究为什么要做分表存储,我们只讲策略模式的实际应用)由于代码实现是实际案例,所以我们基于SpringBoot框架,主要使用了Spring的一些功能,大家要注意了。Step1:定义策略类首先我们定义一个上报接口publicinterfaceAdvertisingDataReported{StringadvertisingDataReported(Objectparam);}Step2:定义一个具体的策略实现类@ServicepublicclassBottomAdvertisingDataReportedimplementsAdvertisingDataReported{@OverridepublicStringadvertisingDataReported(Objectparam){//具体业务逻辑省略returnnull;}}第三步:策略控制类由于策略模式实现的具体策略有很多,具体使用哪种策略取决于我们输入的参数,也就是我们的业务我们如何才能优雅的判断网站的广告类型呢?先来看看这个方法publicstaticvoidmain(String[]args){StringadvertisingType="1";if(advertisingType.equals("1")){//执行策略A}elseif(advertisingType.equals("2")){//执行策略2}}很多人这样写,我们会这里不讨论这些问题。我们先来看看这样写存在哪些问题?存在的问题:1、违反了开闭原则,每增加一个新的策略实现类,都要增加一个if判断;2、随着策略实现类的增多,代码变得臃肿,越来越难以维护;基于这种情况,我们可不可以在项目启动的时候,把所有的策略实现类都初始化好,存储到Map中,以广告类型为key,实现类为Value。让我们看一下下面的代码:@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{//返回该接口的所有实现类MaptempMap=applicationContext.getBeansOfType(RevertisingDataclass);tempMap.values().forEach(strategyService->STRATEGY_MAP.put(strategyService.getClass().getName(),strategyService));}publicAdvertisingDataReportedgetInstance(Classclazz){returnSTRATEGY_MAP.get(clazz.getName());}}我们的策略控制类实现了ApplicationContextAware,你可以理解这个类,它可以获取ApplicationContext的上下文,既然我们说的是SpringBoot中的这种情况,我们的策略类实现类是通过@Service注解注入到Spring容器中的,所以我们可以直接从容器中获取策略类的所有实现类。在所有策略实现类之后,我们以类路径为键,将类实现作为值存储在映射中。至此,我以为大功告成了。你认为问题是什么?我们怎么知道这个条目需要采取哪个特定的策略类?还需要定义一个单独的类来映射广告类型和策略类。这不就是判断的逻辑吗?这种映射关系必须一直保持。Transformation如果不想单独定义一个类来将广告类型和策略类一一映射,那我们可不可以在策略类中解决,每个策略类实现类都知道自己要处理的是哪种类型,所以我们可以把map中的Key类路径的值。具体实现有两种。可以自定义注解,通过添加注解来区分,也可以使用方法,所以我们这里直接使用方法进行处理。修改后的代码:策略类:publicinterfaceAdvertisingDataReported{//新方法AdvertisingTypeEnumadvertisingType();StringadvertisingDataReported(Objectparam);}策略实现类:@ServicepublicclassBottomAdvertisingDataReportedimplementsAdvertisingDataReported{@OverridepublicAdvertisingTypeEnumadvertisingType(revertising){AdvertisingTypeEnum.BOTTOM;}@OverridepublicStringadvertisingDataReported(Objectparam){returnnull;}}策略控制类:@ComponentpublicclassStrategyFactoryimplementsApplicationContextAware{//Mapkey改为广告类型枚举类privatefinalMapSTRATEGY_MAP=newConcurrentHashMap<>();@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{MaptempMap=applicationContext.getBeansOfType(AdvertisingDataReported.class);tempMap.values().forEach(tegyService->STRATEGY_MAP.put(strategyService.advertisingType(),strategyService));}//根据广告类型获取对应的策略类}广告枚举类:publicenumAdvertisingTypeEnum{BOTTOM,TOP;私有字符串广告类型;AdvertisingTypeEnum(){}//setgetomitive}策略类的具体用法@RestControllerpublicclassAdvertisingDataReportedController{@ResourceprivateStrategyFactorystrategyFactory;@RequestMapping(value="/reported/data",method=RequestMethod.POST)publicStringreportedData(AdvertisingTypeEnumadvertisingTypeEnum,Objectobj){AdvertisingDataReporteddataReported=strategyFactory.getInstance(advertisingTypeEnum);Stringresult=dataReported.advertisingDataSReported(ob;nj)}return}小总结:就算我们策略模型的case到这里就结束了,还有几个问题不知道大家有没有疑惑我为什么用Object作为方法的入参?在我们的例子中,各个策略类的入参看起来是一样的,但是也有可能是同一个策略的实现类,只是入参完全可能不一样,所以这个时候,我们可以在方法内部传入对象进行转换。当然,如果你觉得策略方法过于死板,那么你也可以在方法中加入泛型,具体转换成什么类型??,由调用者传入泛型来转换。经过这样的改造,我们刚才遇到的两个问题都不是问题了。我们要添加一个策略实现类,只需要实现定义好的策略类即可,不需要额外添加任何代码。阅读原文