介绍
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
举例:诸葛亮给了赵云三个锦囊,每个锦囊都有一个策略。选择交通工具汽车、火车、飞机都是一种策略。
注意事项:
当我们的if else中需要处理很多业务,就可以考虑使用策略模式;
如果一个系统的策略过多,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现
这里以不同等级的客户购买物品时对应不同的折扣为例,先创建一个接口
publicinterfaceStrategy { DoublecalculatePrice(Doubleprice,Integeramount); }
具体的策略类
publicclassGeneralCustomerimplementsStrategy { publicDoublecalculatePrice(Doubleprice, Integeramount) { returnprice*amount; } } publicclassSilverCustomerimplementsStrategy { publicDoublecalculatePrice(Doubleprice, Integeramount) { returnprice*amount*0.99; } } publicclassGoldCustomerimplementsStrategy { publicDoublecalculatePrice(Doubleprice, Integeramount) { returnprice*amount*0.97; } }
创建 Context 类。上下文并不执行任务, 而是负责与具体的策略类交互。这就好比出行工具有汽车、火车、飞机,但是谁去开火车?就是这个context
publicclassContext { privateStrategystrategy; publicContext(Strategystrategy){ this.strategy=strategy; } publicDoubleexecuteStrategy(Doubleprice,Integeramount){ returnstrategy.calculatePrice(price, amount); } }
使用
publicclassStrategyDemo { publicstaticvoidmain(String[] args) { doubletotalPrice=0; Contextcontext=newContext(newGeneralCustomer()); totalPrice=context.executeStrategy(15.0, 2); System.out.println(totalPrice); Contextcontext1=newContext(newSilverCustomer()); totalPrice=context1.executeStrategy(15.0,2); System.out.println(totalPrice); Contextcontext2=newContext(newGoldCustomer()); totalPrice=context2.executeStrategy(15.0, 2); System.out.println(totalPrice); } }
问题
1、还是要去if else 判断客户的等级然后决定使用哪种策略,如何解决这个问题?
2、策略类会增多,业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑
扩展
策略工厂模式
上一篇我们说到的工厂模式,我给一个标识,由工厂帮我创建对应的产品,那么结合到这里是不是就可以解决上述问题? 我给工厂一个客户等级标识,它会给你对应的策略。
创建策略工厂,Context类就用不上了:
publicclassStrategyFactory { publicstaticStrategyexecute(IntegerlevelCode){ Strategystrategy=null; switch (levelCode){ case1: strategy=newGeneralCustomer(); break; case2: strategy=newSilverCustomer(); break; case3: strategy=newGoldCustomer(); break; default: thrownewIllegalArgumentException("客户等级错误"); } returnstrategy; } }
使用
publicclassStrategyDemo { publicstaticvoidmain(String[] args) { //接口参数获取用户等级 传给策略工厂即可Strategygeneral=StrategyFactory.execute(1); general.calculatePrice(15.0,2); } }
如上我们可以解决第一个问题,那么第二个问题又该如何解决,策略类膨胀会导致项目非常臃肿。
Map+函数式接口
此时我们把上诉的策略类全部作为方法
publicclassCustomerStrategyService { publicDoublegeneralCustomer(Doubleprice, Integeramount) { returnprice*amount; } publicDoublesilverCustomer(Doubleprice, Integeramount) { returnprice*amount*0.99; } publicDoublegoldCustomer(Doubleprice, Integeramount) { returnprice*amount*0.97; } } Map+Function优化publicclassPriceService { privateCustomerStrategyServicecustomerStrategyService; interfaceCalculateFunction<A,B>{ publicDoublecalculate(Aa,Bb); } privatefinalMap<Integer, CalculateFunction<Double,Integer>>strategyMap=newHashMap<>(); /*** 初始化策略*/publicvoidinitDispatcher(){ strategyMap.put(1,(price,amount) ->customerStrategyService.generalCustomer(price,amount)); strategyMap.put(2,(price,amount) ->customerStrategyService.silverCustomer(price,amount)); strategyMap.put(3,(price,amount) ->customerStrategyService.goldCustomer(price,amount)); } publicDoublecalculatePrice(IntegercustomerLevel,Doubleprice,Integeramount){ CalculateFunction<Double, Integer>function=strategyMap.get(customerLevel); //这里防止客户等级没找到匹配的,可以使用断言来判断从而抛出统一异常returnfunction.calculate(price,amount); } }
搞个接口试试
publicclassTestController { privatePriceServicepriceService; "/calculatePrice") (publicDoublecalculatePrice(IntegercustomerLevel,Doubleprice,Integeramount){ returnpriceService.calculatePrice(customerLevel, price, amount); } }
总结
少于4种情况且每种情况对应的业务逻辑不复杂那肯定首选if else,
有4-6种情况且业务逻辑不复杂,建议使用switch case
业务逻辑复杂的情况下,可使用策略工厂模式或者Map+Function,自行选择。