当前位置:首页 > 高端制造 > 正文

设计模式-策略模式详解

1.定义策略模式指的是当处理某个问题有多种方法可以实现时,可以为此定义一组相互替换的算法(策略),使得客户可以根据不同的需求选择不同的算法,将对象和行为分开。2.现实场景出行时可以选择不同的交通工具,这是策略模式的体现。购物时可以选择不同的支付方式,这是策略模式的体现。在软件开发中也会遇到相似的情况...

1.定义策略模式指的是当处理某个问题有多种方法可以实现时,可以为此定义一组相互替换的算法(策略),使得客户可以根据不同的需求选择不同的算法,将对象和行为分开。2.现实场景出行时可以选择不同的交通工具,......

1.定义

策略模式指的是当处理某个问题有多种方法可以实现时,可以为此定义一组相互替换的算法(策略),使得客户可以根据不同的需求选择不同的算法,将对象和行为分开。

2.现实场景

出行时可以选择不同的交通工具,这是策略模式的体现。

购物时可以选择不同的支付方式,这是策略模式的体现。

在软件开发中也会遇到相似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。

3.策略模式原理

在策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以被称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明.而每种算法对应一个具体的策略类.

策略模式的主要角色如下:

抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。用于定义具体策略类的有哪些行为(算法)。

具体策略(ConcreteStrategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。

环境或上下文(Context)类:是使用算法的角色,持有一个抽象策略类的引用,最终给客户端调用。

4.策略模式实现

策略模式的本质是通过Context类来作为中心控制单元,对不同的策略进行调度分配。

/***出行策略接口*/publicinterfaceTravelStrategy{voidtravel();}/***出行策略具体实现飞机AirLine*/publicclassAirLineimplementsTravelStrategy{@Overridepublicvoidtravel(){("airline");}}/***出行策略具体实现轿车car*/publicclassCarStrategyimplementsTravelStrategy{@Overridepublicvoidtravel(){("car");}}/***环境类,策略调用者*/publicclassCustomerContext{//引用了抽象的策略类privateTravelStrategytravelStrategy;publicCustomerContext(TravelStrategytravelStrategy){=travelStrategy;}//调用策略类中方法publicvoidtravel(){();}publicstaticvoidmain(String[]args){//可以在运行时指定类型,通过配置文件+反射机制实现CustomerContextcustomerContext=newCustomerContext(newCarStrategy());();}}
5.策略模式应用实例5.1业务场景

面试问题:如何用设计模式消除代码中的if-else

物流行业中,通常会涉及到EDI报文(XML格式文件)发送和回执,每发送一份EDI报文,后续都会收到与之关联的回执(标识该数据在第三方系统中的流转状态)。

这里列举几种回执类型:MT1101、MT2101、MT4101、MT8101,系统在收到不同的回执报文后,会执行对应的业务逻辑。我们就以回执处理为演示案例。

5.2非设计模式实现

如下代码客户端处理回执信息中出现了很多if-eseif的代码,如果处理每种回执信息的逻辑很复杂,代码将会变得臃肿,可读性和维护性将会变得困难。

/***回执信息类**/publicclassReceipt{privateStringmessage;//回执信息privateStringtype;//回执类型(MT1101、MT2101、MT4101、MT8104)publicReceipt(){}publicReceipt(Stringmessage,Stringtype){=message;=type;}publicStringgetMessage(){returnmessage;}publicvoidsetMessage(Stringmessage){=message;}publicStringgetType(){returntype;}publicvoidsetType(Stringtype){=type;}}/***客户端处理回执信息**/publicclassClient{publicstaticListReceiptgenReceiptList(){//模拟回执信息ListReceiptreceiptList=newArrayList();(newReceipt("MT1101回执","MT1011"));(newReceipt("MT2101回执","MT2101"));(newReceipt("MT4101回执","MT4101"));(newReceipt("MT8104回执","MT8104"));//returnreceiptList;}publicstaticvoidmain(String[]args){ListReceiptreceiptList=genReceiptList();//循环判断for(Receiptreceipt:receiptList){if("MT1011".equals(())){("接收到MT1011回执!");("解析回执内容");("执行业务逻辑A"+"\n");}elseif("MT2101".equals(())){("接收到MT2101回执!");("解析回执内容");("执行业务逻辑B"+"\n");}elseif("MT4101".equals(())){("接收到MT4101回执!");("解析回执内容");("执行业务逻辑C"+"\n");}elseif("MT81041".equals(())){("接收到MT8101回执!");("解析回执内容");("执行业务逻辑D");}//}}}
5.3策略模式优化实现5.3.0依赖
/groupIdartifactIdlombok//version//groupIdartifactIdhutool-all//version/depency!--反射工具--/groupIdartifactIdreflections//version/depency/depencies
5.3.1回执策略接口定义

如下定义了回执策略接口,以及它的两个实现。还定义了回执策略自定义注解

//==========================================回执策略自定义注解================================================/***回执策略自定义注解*/@Target()@Retention()public@interfaceStrategyHandler{Stringvalue()default"default";}//==========================================回执策略接口================================================/***回执策略接口*/publicabstractclassAbstractStrategy{/***定义回执信息处理方法*/protectedabstractvoidhandle(Stringtype,Stringmsg);}//==========================================回执策略具体实现================================================/***MT1101回执策略接口具体实现*/@StrategyHandler(value="MT1101")publicclassMT1101StrategyextsAbstractStrategy{@Overrideprotectedvoidhandle(Stringtype,Stringmsg){("接收到"+type+"回执!");("解析回执内容");("执行业务逻辑A"+"\n");}}/***MT2101回执策略接口具体实现*/@StrategyHandler(value="MT2101")publicclassMT2101StrategyextsAbstractStrategy{@Overrideprotectedvoidhandle(Stringtype,Stringmsg){("接收到"+type+"回执!");("解析回执内容");("执行业务逻辑A"+"\n");}}此次自行补充
5.3.2上下文类定义

如下代码定义回执策略调用的上下文类,内部引用了ReceiptHandlerStrategy类型的对象。

/***策略调用者,上下文类定义*/@DatapublicclassStrategyContext{//回执消息类型privateStringtype;//回执消息privateStringmsg;//引用策略接口引用privateAbstractStrategyabstractStrategy;//策略具体实现所在包privatestaticStringHNADLER_PACKAGE_NAME="";//缓存用于存储具体的策略实现对象privatestaticMapString,AbstractStrategymap=newHashMap();/***策略调用者,上下文类构造器*/publicStrategyContext(Stringtype,Stringmsg){if((map)){//加载指定包下的所有类Reflectionsreflections=newReflections(HNADLER_PACKAGE_NAME);//替换为你的包名SetClass?extsAbstractStrategysubTypes=();//循环解析所有的回执策略类for(Class?extsAbstractStrategysubType:subTypes){//获取回执策略类上注解的值StrategyHandlerannotation=();Stringkey=();if(key==null||"".equals(key)){continue;}Stringstrategyname=();AbstractStrategystrategy;try{Class?clzz=(strategyname);Constructor?declaredConstructor=();(true);strategy=(AbstractStrategy)();}catch(ClassNotFoundExceptione){thrownewRuntimeException(e);}catch(NoSuchMethodExceptione){thrownewRuntimeException(e);}catch(InvocationTargetExceptione){thrownewRuntimeException(e);}catch(InstantiationExceptione){thrownewRuntimeException(e);}catch(IllegalAccessExceptione){thrownewRuntimeException(e);}(key,strategy);}}if(((type))){thrownewRuntimeException(type+"回执处理器创建失败");}abstractStrategy=(type);=msg;=type;}/***消息处理暴露给外部调用*/publicvoidhandle(){(type,msg);}}
5.3.3测试回执处理

如下处理回执信息的ifelseif都消除掉了。

publicclassClient{publicstaticListReceiptgenReceiptList(){//模拟回执信息ListReceiptreceiptList=newArrayList();(newReceipt("MT1101回执","MT1101"));(newReceipt("MT2101回执","MT2101"));(newReceipt("MT4101回执","MT4101"));(newReceipt("MT8101回执","MT8101"));//returnreceiptList;}publicstaticvoidmain(String[]args){ListReceiptreceipts=genReceiptList();for(Receiptreceipt:receipts){StrategyContextstrategyContext=newStrategyContext((),());();}}}
5.3.4总结

经过上面的改造,我们已经消除了if-else的结构,每当新来了一种回执,只需要添加新的回执处理策略。

6.优缺点总结

(1)策略模式优点:

由于策略类都实现同一个接口,策略类之间可以自由切换

易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“

避免使用多重条件选择语句(ifelse),充分体现面向对象设计思想。

(2)策略模式缺点:

客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

7.策略模式使用场景

(1)一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中

本质将算法自身实现逻辑和使用算法的逻辑进行分离

方便对算法逻辑进行优化

方便替换旧算法,采用新算法

(2)一个类定义了多种行为,并且这些行为有很多条件分支,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。

在实际开发中,有许多算法可以实现某一功能,如查找、排序等,通过if-else等条件判断语句来进行选择非常方便。但是这就会带来一个问题:当在这个算法类中封装了大量查找算法时,该类的代码就会变得非常复杂,维护也会突然就变得非常困难。虽然策略模式看上去比较笨重,但实际上在每一次新增策略时都通过新增类来进行隔离,短期虽然不如直接写if-else来得效率高,但长期来看,维护单一的简单类耗费的时间其实远远低于维护一个超大的复杂类。

(3)系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。

如果我们不希望客户知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关数据结构,可以提高算法的保密性与安全性

最新文章