我想大家一定都或多或少看过各种“攻略模式”的讲解、讲道等等吧。本文旨在“厘清”策略模式,并尝试回答以下问题:策略模式如何优化业务逻辑代码结构?为什么要用屠刀杀鸡?我需要使用策略模式的几个ifelse场景?!有没有更好的代码结构来实现策略模式?策略模式如何优化业务逻辑代码结构?要回答这个问题,首先得看一下策略模式的定义,从定义入手去理解。策略模式的教科书定义非常简洁:类的行为或其算法可以在运行时改变。让我们将其减少到代码级别。人性化的翻译一下,我会在运行时给你这个类的方法传递不同的“键”,你的方法会执行不同的业务逻辑。仔细一看,这不就是ifelse做的吗?策略模式优化了什么?其实策略模式的核心思想和ifelse是一模一样的,根据不同的key动态的寻找不同的业务逻辑,那么仅此而已吗?其实我们调用的策略模式其实就是调整代码结构,使用接口+实现类+调度逻辑,让代码结构更易维护。一般教材会以接口和实现类结尾,其他博客会提到dispatch逻辑。我不会在这里冗长。综上所述,就算是用了strategy模式,你该写的业务逻辑照常写,逻辑派发的时候还是变相的ifelse。而它的优化点就是将接口抽象出来,将业务逻辑一个一个封装到实现类中,任意替换。在复杂场景下(更多业务逻辑),比直接ifelse更容易维护。为什么要用屠刀杀鸡?我需要使用策略模式的几个ifelse场景?!我想朋友们经常会有这样的不满。我的业务逻辑只有3或4行。你给我很多类定义?有必要这么麻烦吗?我觉得具体的业务逻辑需要在不同的类中,简单点就好了。其实我们不满意的是策略模型带来的不足:1.策略类的数量会增加。2、业务逻辑分散到各个实现类中,没有地方可以忽略整个业务逻辑。针对传统策略模型的不足,这里分享一个实现思路,这个思路帮助我们团队解决了多个复杂的ifelse业务场景,也比较容易理解。代码需要利用Java8的特点——使用Map和函数式接口来实现。直接展示代码结构:为了简单演示一个思路,代码使用String类型模拟一个业务BO。其中:1.getCheckResult()为传统方法2.getCheckResultSuper()定义了“判断条件”和“业务逻辑”的映射关系,具体解释见代码注释/某业务服务类/@Servicepublic类BizService{/传统的ifelse方案当每个业务逻辑有3、4行时,不值得使用传统的策略模式,直接的ifelse难以阅读/publicStringgetCheckResult(Stringorder){if("Check1".equals(order)){return"执行业务逻辑1";}elseif("Check2".equals(order)){return"Executebusinesslogic2";}elseif("Check3".equals(order)){return"Executebusinesslogic3";}elseif("Check4".equals(order)){return"Executebusinesslogic4";}elseif("Check5".equals(order)){return"Executebusinesslogic5";}elseif("Check6".equals(order)){return"Executebusinesslogic6";}elseif("Check7".equals(order)){return"Executebusinesslogic7";}elseif("Check8".equals(order)){return"Executebusinesslogic8";}elseif("Check9".equals(order)){return"Executebusinesslogic9";}返回“不在returnabusinesserrorinthelogicallogic";}/业务逻辑将MapFunction赋值为函数式接口,下面代码中Function的意思是接收一个Stirng类型的变量,返回一个类型的结果String/privateMap>checkResultDispatcher=newHashMap<>();/初始化业务逻辑dispatchMap,其中值存储为lambda表达式/@PostConstructpublicvoidcheckResultDispatcherInit(){checkResultDispatcher.put("check1",order->String.format("Executebusinesslogic1for%s",order));checkResultDispatcher.put("Check2",order->String.format("Executebusinesslogic2for%s",order));checkResultDispatcher.put("Check3",order->String.format("Executebusinesslogic3for%s",order));checkResultDispatcher.put("Check4",order->String.format("Executebusinesslogic4for%s",order));checkResultDispatcher.put("Check5",order->String.format("Executebusinesslogic5代表%s",order));checkResultDispatcher.put("Check6",order->String.format("Executebusinesslogic6for%s",order));checkResultDispatcher.put("Check7",order->String.format("为%s执行业务逻辑7",order));checkResultDispatcher.put("Check8",order->String.format("Executebusinesslogic8for%s",order));checkResultDispatcher.put("Check9",order->String.format("Executebusinesslogic9for%s",order));}publicStringgetCheckResultSuper(Stringorder){//从逻辑调度Dispatcher中获取业务逻辑代码,result变量为lambda表达式Functionresult=checkResultDispatcher.get(order);if(result!=null){//执行这个表达式得到String类型的resultreturnresult.apply(order);}return"不返回处理逻辑中的业务错误";}}复制代码通过http调用看看效果:/*模拟一个http调用*/@RestControllerpublicclassBizController{@AutowiredprivateBizServicebizService;@PostMapping("/v1/biz/testSuper")publicStringtest2(Stringorder){returnbizService.getCheckResultSuper(order);}}复制代码这是一个简单的demo演示,后面会给出一些复杂的场景,问题会比较多。”我们来看看这样的实现有什么好处,会带来什么问题。好处很直观:在一段代码中,可以直观的看到“判断条件”与业务逻辑的映射关系。不需要单独定义接口和实现类,直接使用已有的函数式接口即可(什么?不懂函数式接口?继续理解),实现类直接就是业务代码本身。缺点:1.团队成员需要懂lambda表达式(什么?Java14出来了,还有没有用过Java8新特性的小伙伴?)接下来我列举几个业务中经常遇到的Ifelse场景,以及在实际业务场景中用Map+函数式接口解决它的问题有的小伙伴会说,我的判断条件是多种多样的,很复杂。你之前举了一个例子,只有一个判断逻辑,但是我有多个判断逻辑怎么办?很容易解决:写一个判断逻辑的方法,Map的key通过方法计算/一个业务服务类/@ServicepublicclassBizService{privateMap>checkResultDispatcherMuti=newHashMap<>();/初始化业务逻辑调度映射,其中值存储为lambda表达式/@PostConstructpublicvoidcheckResultDispatcherMuitInit(){checkResultDispatcherMuti.put("key_order1",order->String.format("Executebusinesslogic1for%s",命令));checkResultDispatcherMuti.put("key_order1_order2",order->String.format("Executebusinesslogic2for%s",order));checkResultDispatcherMuti.put("key_order1_Order2_Order3",order->String.format("Executebusinesslogic3for%s",order));}publicStringgetCheckResultMuti(Stringorder,intlevel){//写一个逻辑来生成key:Stringley=getDispatcherKey(order,level);函数<字符串,String>结果=checkResultDispatcherMuti.get(ley);if(result!=null){//执行这个表达式得到String类型的结果returnresult.apply(order);}return"不返回处理逻辑中的业务错误";}/*判断条件的方法*/privateStringgetDispatcherKey(Stringorder,intlevel){StringBuilderkey=newStringBuilder("key");for(inti=1;i<=level;i++){key.append("_"+order+i);}返回键.toString();}}复制代码,使用http模拟:/*模拟一次http调用*/@RestControllerpublicclassBizController{@AutowiredprivateBizServicebizService;@PostMapping("/v1/biz/testMuti")publicStringtest1(Stringorder,Integerlevel){returnbizService.getCheckResultMuti(order,level);}}只需设计您的密钥生成规则可能有朋友会问:我的业务逻辑很多很多行,直接写在checkResultDispatcherMuitInit()方法的Map中不会很长吗?直接写当然就长了,我们可以抽象出一个service服务把业务逻辑放在里面,然后在定义中调用:提供业务逻辑单元:/*提供业务逻辑单元*/@ServicepublicclassBizUnitService{publicStringbizOne(Stringorder){returnorder+"各种花式操作1";}publicStringbizTwo(Stringorder){returnorder+"各种花式操作2";}publicStringbizThree(Stringorder){returnorder+"各种花式操作3";}}复制代码/一个业务服务类/@ServicepublicclassBizService{@AutowiredprivateBizUnitServicebizUnitService;privateMap>checkResultDispatcherComX=newHashMap<>();/初始化业务逻辑调度映射,其中值存储为lambda表达式/@PostConstructpublicvoidcheckResultDispatcherComXInit(){checkResultDispatcherComX.put("key_order1",order->bizUnitService.bizOne(order));checkResultDispatcherComX.put("key_order1_order2",order->bizUnitService.bizTwo(order));检查结果tDispatcherComX.put("key_order1_order2_order3",order->bizUnitService.bizThree(order));}publicStringgetCheckResultComX(Stringorder,intlevel){//写一段逻辑生成key:Stringley=getDispatcherComXKey(order,level);Function结果=checkResultDispatcherComX.get(ley);if(result!=null){//执行这个表达式得到String类型的resultreturnresult.apply(order);}return"不返回处理逻辑中的业务错误";}/*判断条件的方法*/privateStringgetDispatcherComXKey(Stringorder,intlevel){StringBuilderkey=newStringBuilder("key");for(inti=1;i<=level;i++){key.append("_"+order+i);}返回键.toString();}}复制代码调用结果:总结最后,我们尝试回答以下问题:1.策略模式如何优化业务逻辑代码结构?对接口进行抽象,将业务逻辑一一封装到实现类中。在复杂场景下(更多业务逻辑),比直接ifelse更容易维护。为什么要用屠刀杀鸡?我需要使用策略模式的几个ifelse场景?!我们不满意的其实是传统接口实现的缺点:1、策略类会比较多。2、业务逻辑分散到各个实现类中,没有地方可以忽略整个业务逻辑。有没有更好的代码结构来实现策略模式?针对传统策略模型的不足,分享了使用Map和函数式接口实现的思路。