策略模式

# 策略模式 > 策略模式(Strategy Pattern)也叫做政策模式(Policy Pattern),它是将定义的算法封装起来, >让它们之间可以相互替换,从而让算法的变化不影响到使用算法的用户;它属于行为型模式。可以在一定程度上规避 >if-else/switch等 >策略模式使用的面向对象的继承和多态机制,从而实现同一行为在不同的场景下具备不同的实现; ## 应用 策略模式在实际应用场景中有很多的应用,凡是设计到选择的场景基本都可以使用策略模式来实现;比如购买一个商品选择支付方式 ,是选择银行卡还是快捷支付,微信?支付宝等; - 假如一个系统中有很多类,而它们的区别仅仅在与它们的行为不同; - 一个系统需要动态的从几种算法中选择一种;一些平台型产品中肯定会用到的 - 需要屏蔽算法规则; ## 策略模式类图 ![](https://oscimg.oschina.net/oscnet/up-bcd8e7bee16acac3b254b110a2c2f3796b8.png) 通过上图可以看到,策略模式主要包含3个角色: - **上下文角色(Context)**: 用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略/算法的直接访问,封装可能 存在的变化 - **抽象策略角色(IStrategy)**: 规定策略或算法的行为 - **具体的策略角色(ConcreteStrategy)**: 具体的策略逻辑或算法 > 这里的上下文角色仅仅是一个称谓,大家只要知道他在策略模式中的作用就可以了,就是桥接客户端和策略/方法的作用; ## 举个例子 > 背景: 电商场景下,用户购买一个商品,支付时可以选择一种优惠策略,如果没有优惠,则使用默认的策略,即无任何优惠; ![](https://oscimg.oschina.net/oscnet/up-1f96ebedc89c21db9af9a2ae899918d9fb8.png) 1、上面两层是定义的一个营销策略的接口,然后提供不同的策略来实现; 2、第三层是策略生成所使用的工厂和客户端调用策略中间的上下文,承接上下,桥接模式; 3、最下层的Demo可以看成是客户端; ### 定义策略接口 制定标准方法 ```text public interface IPromotionStrategy { void doPromote(); } ``` ### CashBackStrategy 现金折返策略 ```text public class CashBackStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("直接返现"); } } ``` ### CouponStrategy 优惠券策略 ```text public class CouponStrategy implements IPromotionStrategy{ // 优惠券策略 @Override public void doPromote() { System.out.println("使用优惠券抵扣"); } } ``` ### GroupBuyStrategy 团购优惠策略 ```text public class GroupBuyStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("团购 5人成团"); } } ``` ### EmptyStrategy 默认策略 ```text public class EmptyStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("无任何优惠"); } } ``` ### PromoteActivity 客户端和具体算法之间的上下文,用于桥接 ```text public class PromoteActivity { private IPromotionStrategy promotionStrategy; public PromoteActivity(IPromotionStrategy promotionStrategy) { this.promotionStrategy = promotionStrategy; } public void execute(){ promotionStrategy.doPromote(); } } ``` ### PromoteStrategyFactory 策略工厂 ```text public class PromoteStrategyFactory { private PromoteStrategyFactory() { } // 策略容器 private static Map<String, IPromotionStrategy> promotionStrategyMap = new HashMap<>(); // 默认空策略-没有任何优惠 private static IPromotionStrategy emptyStrategy = new EmptyStrategy(); // 这个可以放到配置文件或者放到数据库,在项目启动的时候加载到服务中就可以了 static { promotionStrategyMap.put(PromoteKey.CASH_BACK, new CashBackStrategy()); promotionStrategyMap.put(PromoteKey.COUPON, new CouponStrategy()); promotionStrategyMap.put(PromoteKey.CROUP_BUY, new GroupBuyStrategy()); } // 简单工厂创建营销策略 public static IPromotionStrategy createPromoteStrategy(String key) { IPromotionStrategy promotionStrategy = promotionStrategyMap.get(key); if (promotionStrategy == null) { return emptyStrategy; } return promotionStrategy; } // 所有定义好的策略 private interface PromoteKey { String COUPON = "coupon"; String CROUP_BUY = "groupBuy"; String CASH_BACK = "cashBack"; } // 因为策略模式需要用户知道所有可用的策略,所以这个方法暴露给客户端 public static Set<String> getPromoteKeySet() { return promotionStrategyMap.keySet(); } } ``` ### 策略模式下客户端写法 ```text public static void main(String[] args) { String strategy = PromoteStrategyFactory.getPromoteKeySet().stream().findAny().get(); IPromotionStrategy promoteStrategy = PromoteStrategyFactory.createPromoteStrategy(strategy); promoteStrategy.doPromote(); } ``` ### 非策略模式下客户端写法 ```text public static void main(String[] args) { String strategy = "客户选择的策略"; IPromotionStrategy promotionStrategy; if ("团购".equals(strategy)){ promotionStrategy = new GroupBuyStrategy(); }else if ("优惠券".equals(strategy)){ promotionStrategy = new CouponStrategy(); }else if ("现金折返".equals(strategy)){ promotionStrategy = new CashBackStrategy(); }else { promotionStrategy = new EmptyStrategy(); } promotionStrategy.doPromote(); } ``` 以上的对比,哪种方式比较优雅,立见高下了吧😄😄😄 ## 策略模式的优点 - **策略模式是符合开闭原则的** - **避免使用多重条件转移语句 if-else语句、switch语句等** - **使用策略模式可以提高算法的保密性和安全性(客户端通过上下文组建来调用具体的算法-桥接)** ## 策略模式的缺点 - **客户端必须要知道全部可用的策略,然后由用户决定使用那个策略,决定权在于客户** - **代码中会产生很多的策略类,增加代码量和系统的维护难度** ## 👤个人体会 1、结合业务场景来设计,体会设计模式的思路,学设计模式最重要的是思想; 2、设计模式一般在框架中体现的比较多,大家可以多学习一些框架源码的设计理念,如果你是初级,可能一下子理解不了 ,慢慢体会吧,当你工作一段时间之后,见过一些工业生产的项目,看过一些优秀的框架源码的时候,会有一个 "柳暗花明又一村" 的阶段; 3、举个例子,Controller我们都写过的,一个项目中的接口路径是唯一的,项目启动的时候,由Spring加载到容器中, 当由请求进来时是,Spring是根据path来返回具体的控制器,也就是Controller具体的方法; 4、还有Comparator比较器,不同的容器内部做数据排序的时候由不同的实现,这也是策略模式的体现; 5、本文中的例子虽然简单,但是包含了简单工厂模式,桥接模式,策略模式。任何一种设计模式都难以独立存在,这个大家要注意;