当前位置: 首页 > 科技观察

设计模式系列策略模式

时间:2023-03-21 22:22:23 科技观察

最近有个学妹跟我交流如何有效避免代码中一长串的ifelse判断或者switch条件判断?对于更多的答案,使用设计来避免这个问题是合理的。在设计模式中,可以使用工厂模式或者策略模式来处理这类问题。之前分享过工厂模式,有兴趣的同学可以回顾一下。设计模式系列之前的文章:单例模式工厂模式ProcessEngineBuilder模式Prototype模式ChainofResponsibilityMode观察者模式那么工厂模式和Strategy模式有什么区别呢?FactoryMode是一种创建型设计模式,主要用于不同的Type创建不同的对象,实现类对象的解耦。策略模式是一种行为设计模式。主要是针对不同的策略做出相应的行为,实现行为解耦。这一次,我们就来说说策略模式是如何实现行为解耦的。大纲定义什么是策略模式?它的实现原理是怎样的?定义一系列算法,封装每个算法,并使它们可以互换。不同的策略可以使算法独立于使用它们的客户而改变。上面的定义来自于设计模式之美,感觉有点抽象?先看一张结构图。):运行特定的策略类。这样一来,结构其实并不复杂,和state模式差不多。那么这段代码是如何实现的呢?比如,大家都很熟悉汽车。希望每个人都能早日实现自己的汽车梦想。汽车的不同具体策略就像不同的策略。驾驶员选择哪个档位,汽车将以档位的速度前进,整个选择权在驱动程序(上下文)手中。publicinterfaceGearStrategy{//定义策略执行方法voidalgorithm(Stringparam);}首先在这里以接口的形式定义抽象策略,还有一种写法是用抽象方法abstract,也是一样的。这取决于每个人的选择。publicabstractclassGearStrategyAbstract{//定义策略实现方法abstractvoidalgorithm(Stringparam);}publicclassGearStrategyOneimplementsGearStrategy{@Overridepublicvoidalgorithm(Stringparam){System.out.println("currentgear"+param);}}其次,定义具体的齿轮策略实现算法方法。publicclassContext{//缓存所有的策略,目前是无状态的,可以共享策略类对象privatestaticfinalMapstrategies=newHashMap<>();//第一种写法static{strategies.put("one",newGearStrategyOne());}publicstaticGearStrategygetStrategy(Stringtype){if(type==null||type.isEmpty()){thrownewIllegalArgumentException("typeshouldnotbeempty.");}returnstrategies.get(type);}//第二种写法publicstaticGearStrategygetStrategySecond(Stringtype){if(type==null||type.isEmpty()){thrownewIllegalArgumentException("typeshouldnotbeempty.");}if(type.equals("one")){returnnewGearStrategyOne();}returnnull;}publicstaticvoidmain(String[]args){//测试结果GearStrategystrategyOne=Context.getStrategy("one");strategyOne.algorithm("1档");//结果:当前档位1GearStrategystrategyTwo=Context.getStrategySecond("one");strategyTwo.algorithm("1gear");//result:当前档位为1gear}}最后是实现运行环境(Context),可以将其定义为StrategyFactory,但它们的含义相同。在main方法中的测试demo中,可以看到不同的类型可以实现不同的策略,这就是策略模式的主要思想。Context中定义了两种写法:第一种是维护一个策略的Map容器??。这样就需要判断每个策略是否可以共享使用,只作为算法实现使用。第二种是直接通过有状态类,每次根据类型new一个策略类对象。这需要根据实际业务场景进行判断。框架的应用策略模式也体现在框架中一个很常见的地方,想必大家都用过。即JDK中的线程池ThreadPoolExecutor首先定义了一个类似这样的线程池,实现了线程池的异常策略。这个线程池的异常策略就是策略模式的思想。在源码中有一个抽象的异常策略接口RejectedExecutionHandler,它也有四种拒绝策略。关系图如下:这个体现在框架上。根据自己的业务场景,合理选择线程池的异常策略。业务转型的例子在实际业务场景中仍然有大量应用。社交电商中分享产品是一个非常重要的环节。假设现在我们需要实现一个分享图片的功能,比如目前有单品、多品、订单、场地、邀请、小程序链接等多种分享场景。在线流程图,先用ifelse语句做一个普通的业务代码判断,就像下面这个方法:is"+param);}}publicclassMultiItemShare{//多商品publicvoidalgorithm(Stringparam){System.out.println("当前分享的图片为"+param);}}publicclassOrderItemShare{//下单publicvoidalgorithm(Stringparam){System.out.println("当前分享图片为"+param);}}publicclassShareFactory{publicstaticvoidmain(String[]args)throwsException{IntegershareType=1;//测试业务逻辑if(shareType.equals(ShareType.SINGLE.getCode())){SingleItemSharesingleItemShare=newSingleItemShare();singleItemShare.algorithm("单个项目");}elseif(shareType.equals(ShareType.MULTI.getCode())){MultiItemSharemultiItemShare=newMultiItemShare();multiItemShare.algorithm("多项");}elseif(shareType.equals(ShareType.ORDER.getCode())){OrderItemShareorderItemShare=newOrderItemShare();orderItemShare.algorithm("Order");}else{thrownewException("Unknownsharetype");}//.....省略更多分享场景}enumShareType{SINGLE(1,"单品"),MULTI(2,"多品"),ORDER(3,"下单");/***场景对应代码*/privateIntegercode;/***业务场景说明*/privateStringdesc;ShareType(Integercode,Stringdesc){this.code=code;this.desc=desc;}publicIntegergetCode(){returncode;}//省略getset方法}}这里可以看到每增加一个新的分享类型,都要加一个ifelse判断。如果有十几个场景,整体代码会很长,看起来不是很舒服接下来看看如何用策略模式重构:publicinterfaceShareStrategy{//定义分享策略实现方法voidshareAlgorithm(Stringparam);}publicclassOrderItemShareimplementsShareStrategy{@OverridepublicvoidshareAlgorithm(Stringparam){System.out.println("当前分享的图片是"+param);}}//省略MultiItemShare和SingleItemShare策略//共享工厂publicclassShareFactory{//定义策略枚举enumShareType{SINGLE("single","singleitem"),MULTI("multi","multipleitems"),ORDER("order","order");//场景对应的代码privateStringcode;//业务场景描述privateStringdesc;ShareType(Stringcode,Stringdesc){this.code=code;this.desc=desc;}publicStringgetCode(){returncode;}//省略getset方法}//定义策略映射缓存privatestaticfinalMapshareStrategies=newHashMap<>();static{shareStrategies.put("order",newOrderItemShare());shareStrategies.put("单个",newSingleItemShare());shareStrategies.put("multi",newMultiItemShare());}//获取指定策略publicstaticShareStrategygetShareStrategy(Stringtype){if(type==null||type.isEmpty()){thrownewIllegalArgumentException("typeshouldnotbeempty.");}returnshareStrategies.get(type);}publicstaticvoidmain(String[]args){//testdemoStringshareType="order";ShareStrategyshareStrategy=ShareFactory.getShareStrategy(shareType);shareStrategy.shareAlgorithm("order");//输出结果:当前分享的图片为order}}这里已经转换了策略模式。在客户端请求端,没有那么多ifelse的判断,只需要传入相应的策略方法即可。这里我们维护一个策略缓存映射。直接调用ShareFactory获取策略时,直接从另一个方法中获取策略对象。这就达到了解耦行为的想法。同时也避免了长串的ifelse判断。优点:算法策略可以自由切换。良好的可扩展性。添加一个策略只需要添加一个类。缺点:如果策略类很多,需要维护一个策略枚举,让别人知道你目前有哪些策略。整体看起来其实还是比较简单的,还是那句话学习设计模式,我们还是要学习每一种设计模式的思想,任何一种设计模式的存在都是合理的。当然,不要因为设计模式而去设计代码,那样会得不偿失。