作者:若采 作者案:本文介绍的是StrategyPattern(策略模式)。干货满满,希望阅读后你能有所收获目的 做一件事情有不同的实现方式,可以将变化的部分和不变的部分剥离开,去除大量的ifelse,提供高扩展性。例子代码 比如我们想要带妹吃鸡,就要成为一个神枪手。在各种枪战游戏中,有各种不同的枪,我们要根据射程的不同选择不同的枪进行射击。 如果枪的子弹数量都不富裕,我们要用最少的子弹,最合适的方法达到最强伤害,最终大吉大利。 当我们距离对手:1米以内,使用平底锅(想我当时三级头三级甲,手持AKM,满血满状态,三级包里药包无数,到了决赛圈被平底锅堵在墙角打死啦);100米左右,使用冲锋枪;超过1000米,使用狙击枪(对于我这样的小菜鸡,基本流程是开一枪没打中,暴露位置,被别人一狙打死。。。囧)。面条式代码判断最强武器publicclassNoodlesKillProcessor{根据距离判断最好的武器击杀对手paramdistanceBadSmellpublicstaticvoidkillByDistance(intdistance){if(distance0){thrownewRuntimeException(距离咋还能是负数呢?);}if(distance0distance1){System。out。println(发现敌人);System。out。println(两步快速走过去);System。out。println(掏出平底锅呼他);}if(distance1distance10){System。out。println(发现敌人);System。out。println(快速走过去);System。out。println(掏出手枪打他);}if(distance10distance100){System。out。println(发现敌人);System。out。println(身体站直,心态稳住);System。out。println(掏出冲锋枪打他);}if(distance100distance1000){System。out。println(发现敌人);System。out。println(身体蹲下降低后坐力);System。out。println(掏出步枪);System。out。println(打开3倍镜);System。out。println(开枪射击);}if(distance1000){System。out。println(发现敌人);System。out。println(趴在草丛里苟着);System。out。println(掏出狙击枪);System。out。println(打开8倍镜);System。out。println(开枪射击);}}}问题分析 我觉得这有3个问题,具体分析如下: 01可读性问题 我看这么多ifelse语句,里面的sout语句目前三四行也还好,如果我们有上百行的语句,里面也有很多ifelse,这样都不知道下个主if跑哪去啦 02重复性问题 全都需要发现敌人,如果发现敌人是个成百上千行代码,就很麻烦啦。 03可维护性问题 如果这时候我们新增了一种枪,比如是霰弹枪,适用10到20的时候使用,这时候我们就需要在加一个if语句如下:面条式代码判断最强武器publicclassNoodlesKillProcessor{根据距离判断最好的武器击杀对手paramdistanceBadSmellpublicstaticvoidkillByDistance(intdistance){if(distance0){thrownewRuntimeException(距离咋还能是负数呢?);}if(distance0distance1){System。out。println(发现敌人);System。out。println(两步快速走过去);System。out。println(掏出平底锅呼他);}if(distance1distance10){System。out。println(发现敌人);System。out。println(快速走过去);System。out。println(掏出手枪打他);}if(distance10distance20){System。out。println(发现敌人);System。out。println(身体站直,瞄准);System。out。println(打一枪算一枪);}if(distance20distance100){System。out。println(发现敌人);System。out。println(身体站直,心态稳住);System。out。println(掏出冲锋枪打他);}if(distance100distance1000){System。out。println(发现敌人);System。out。println(身体蹲下降低后坐力);System。out。println(掏出步枪);System。out。println(打开3倍镜);System。out。println(开枪射击);}if(distance1000){System。out。println(发现敌人);System。out。println(趴在草丛里苟着);System。out。println(掏出狙击枪);System。out。println(打开8倍镜);System。out。println(开枪射击);}}} 这个看着也没啥大问题的样子,不就是加了个if么,但是由于我们改动了这个文件,测试同学问我们需要测试哪些功能,说是测一种枪需要5天 问题来啦,本来说是你增加一种枪,需要测5天,但是现在你说改了这文件,上下可能有些局部变量共享的,或者有些方法可能改了入参的值,这些有负作用的方法被调用啦,所以可能狙击枪也得测一测,可能手枪也得测一测。 测试同学崩了,本来5天的工作量,搞成了56天,一个月都在测枪 初步尝试解决 我们先定义好一个基础类,解决一下可读性问题和重复性问题。 定义一个基础武器类:抽象的枪publicabstractclassWeapon{发现敌人protectedvoidfindEnemy(){System。out。println(发现敌人);}开枪前的动作protectedabstractvoidpreAction();开枪protectedabstractvoidshoot();整体的动作publicvoidkill(){findEnemy();preAction();shoot();}} 逐个实现武器的具体类、平底锅、冲锋枪、步枪等类如下:平底锅publicclassPanextendsWeapon{OverrideprotectedvoidpreAction(){System。out。println(两步快速走过去);}Overrideprotectedvoidshoot(){System。out。println(掏出平底锅呼他);}}手枪类publicclassPistolextendsWeapon{OverrideprotectedvoidpreAction(){System。out。println(快速走过去);}Overrideprotectedvoidshoot(){System。out。println(掏出手枪打他);}}霰弹枪publicclassShotgunextendsWeapon{OverrideprotectedvoidpreAction(){System。out。println(身体站直,瞄准);}Overrideprotectedvoidshoot(){System。out。println(打一枪算一枪);}}狙击枪publicclassSniperRifleextendsWeapon{OverrideprotectedvoidpreAction(){System。out。println(趴在草丛里苟着);System。out。println(掏出狙击枪);System。out。println(打开8倍镜);}Overrideprotectedvoidshoot(){System。out。println(开枪射击);}}冲锋枪publicclassSubmachineGunextendsWeapon{OverrideprotectedvoidpreAction(){System。out。println(身体站直,心态稳住);}Overrideprotectedvoidshoot(){System。out。println(掏出冲锋枪打他);}} 我们的方法就可以改动得更清晰啦抽象出类代码判断最强武器publicclassWeaponKillProcessor{根据距离判断最好的武器击杀对手paramdistanceBadSmellpublicstaticvoidkillByDistance(intdistance){if(distance0){thrownewRuntimeException(距离咋还能是负数呢?);}Wif(distance0distance1){weaponnewPan();}elseif(distance1distance10){weaponnewPistol();}elseif(distance10distance20){weaponnewShotgun();}elseif(distance20distance100){weaponnewSubmachineGun();}elseif(distance100distance1000){weaponnewRifle();}elseif(distance1000){weaponnewSniperRifle();}weapon。kill();}} 类图如下: 使用策略模式 上面的代码没有解决最根本的问题,也就是去除ifelse,所用的方法其实就是将ifelse转换为for,这样的代码后续添加枪就不需要再增加新的类型啦。 我们先定义一个通用的策略模式接口如下:策略模式publicinterfaceStrategyTextendsAbstractStrategyRequest,RextendsAbstractStrategyResponse{执行策略paramrequestreturnRexecuteStrategy(Trequest);} 入参和出参都是基本的抽象类:策略模式抽象入参publicabstractclassAbstractStrategyRequest{}策略模式抽象出参publicabstractclassAbstractStrategyResponse{} 实现一个武器抽象类实现接口:publicabstractclassWeaponStrategyimplementsStrategyWeaponStrategyRequest,AbstractStrategyResponse{发现敌人protectedvoidfindEnemy(){System。out。println(发现敌人);}开枪前的动作protectedabstractvoidpreAction();开枪protectedabstractvoidshoot();获取距离范围returnprotectedabstractRangeIntegerqueryDistanceRange();整体的动作publicvoidkill(){findEnemy();preAction();shoot();}OverridepublicAbstractStrategyResponseexecuteStrategy(WeaponStrategyRequestrequest){System。out。println(距离敌人request。getDistance());kill();}} 其中的Range类实现如下:范围类paramTDataAllArgsConstructorpublicclassRangeTextendsComparableT{privateTprivateTpublicRange(Tstart,Tend){this。this。}privatebooleanisIncludeSprivatebooleanisIncludeE判断是否在范围内paramtargetreturnpublicbooleaninRange(Ttarget){if(isIncludeStart){if(start。compareTo(target)0){}}else{if(start。compareTo(target)0){}}if(isIncludeEnd){if(end。compareTo(target)0){}}else{if(end。compareTo(target)0){}}}} 依次实现这个抽象武器策略类:平底锅publicclassPanStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(二步快速走过去);}Overrideprotectedvoidshoot(){System。out。println(掏出平底锅呼他);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(0,1);}}手枪类publicclassPistolStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(快速走过去);}Overrideprotectedvoidshoot(){System。out。println(掏出手枪打他);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(1,10);}}步枪publicclassRifleStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(身体蹲下降低后坐力);System。out。println(掏出步枪);System。out。println(打开3倍镜);}Overrideprotectedvoidshoot(){System。out。println(开枪射击);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(100,1000);}}霰弹枪publicclassShotgunStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(身体站直,瞄准);}Overrideprotectedvoidshoot(){System。out。println(打一枪算一枪);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(10,20);}}狙击枪publicclassSniperRifleStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(趴在草丛里苟着);System。out。println(掏出狙击枪);System。out。println(打开8倍镜);}Overrideprotectedvoidshoot(){System。out。println(开枪射击);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(1000,Integer。MAXVALUE);}}冲锋枪publicclassSubmachineGunStrategyextendsWeaponStrategy{OverrideprotectedvoidpreAction(){System。out。println(身体站直,心态稳住);}Overrideprotectedvoidshoot(){System。out。println(掏出冲锋枪打他);}OverrideprotectedRangeIntegerqueryDistanceRange(){returnnewRange(20,100);}} 定义一个上下文类来对入参进行路由:策略上下文,用来路由策略publicclassStrategyContext{publicstaticfinalListWeaponStrategyWEAPONSTRATEGYSnewArrayList();static{WEAPONSTRATEGYS。add(newPanStrategy());WEAPONSTRATEGYS。add(newPistolStrategy());WEAPONSTRATEGYS。add(newRifleStrategy());WEAPONSTRATEGYS。add(newShotgunStrategy());WEAPONSTRATEGYS。add(newSniperRifleStrategy());WEAPONSTRATEGYS。add(newSubmachineGunStrategy());}publicstaticvoidexecute(Integerdistance){WEAPONSTRATEGYS。stream()。filter((weaponStrategy{RangeIntegerintegerRangeweaponStrategy。queryDistanceRange();returnintegerRange。inRange(distance);}))。findAny()。get()。executeStrategy(newWeaponStrategyRequest(distance));}} 最后在主方法里面调用就好啦:publicclassApp{publicstaticvoidmain(String〔〕args){StrategyContext。execute(89);}} 结果如下:距离敌人89 发现敌人 身体站直,心态稳住 掏出冲锋枪打他 类图如下: 加入我们 我们来自字节跳动飞书商业应用研发部(LarkBusinessApplications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们关注的产品领域主要在企业经验管理软件上,包括飞书OKR、飞书绩效、飞书招聘、飞书人事等HCM领域系统,也包括飞书审批、OA、法务、财务、采购、差旅与报销等系统。 点击链接,欢迎各位加入我们!