策略模式
策略模式(Strategy Pattern)也叫做政策模式(Policy Pattern),它是将定义的算法封装起来,
让它们之间可以相互替换,从而让算法的变化不影响到使用算法的用户;它属于行为型模式。可以在一定程度上规避
if-else/switch等
策略模式使用的面向对象的继承和多态机制,从而实现同一行为在不同的场景下具备不同的实现;
应用
策略模式在实际应用场景中有很多的应用,凡是设计到选择的场景基本都可以使用策略模式来实现;比如购买一个商品选择支付方式,是选择银行卡还是快捷支付,微信?支付宝等;
- 假如一个系统中有很多类,而它们的区别仅仅在与它们的行为不同;
- 一个系统需要动态的从几种算法中选择一种;一些平台型产品中肯定会用到的
- 需要屏蔽算法规则;
策略模式类图
通过上图可以看到,策略模式主要包含3个角色:
- 上下文角色(Context): 用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略/算法的直接访问,封装可能
存在的变化
- 抽象策略角色(IStrategy): 规定策略或算法的行为
- 具体的策略角色(ConcreteStrategy): 具体的策略逻辑或算法
这里的上下文角色仅仅是一个称谓,大家只要知道他在策略模式中的作用就可以了,就是桥接客户端和策略/方法的作用;
举个例子
背景: 电商场景下,用户购买一个商品,支付时可以选择一种优惠策略,如果没有优惠,则使用默认的策略,即无任何优惠;
1、上面两层是定义的一个营销策略的接口,然后提供不同的策略来实现;
2、第三层是策略生成所使用的工厂和客户端调用策略中间的上下文,承接上下,桥接模式;
3、最下层的Demo可以看成是客户端;
定义策略接口 制定标准方法
1 2 3
| public interface IPromotionStrategy { void doPromote(); }
|
CashBackStrategy 现金折返策略
1 2 3 4 5 6
| public class CashBackStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("直接返现"); } }
|
CouponStrategy 优惠券策略
1 2 3 4 5 6 7
| public class CouponStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("使用优惠券抵扣"); } }
|
GroupBuyStrategy 团购优惠策略
1 2 3 4 5 6
| public class GroupBuyStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("团购 5人成团"); } }
|
EmptyStrategy 默认策略
1 2 3 4 5 6
| public class EmptyStrategy implements IPromotionStrategy{ @Override public void doPromote() { System.out.println("无任何优惠"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class PromoteActivity {
private IPromotionStrategy promotionStrategy;
public PromoteActivity(IPromotionStrategy promotionStrategy) { this.promotionStrategy = promotionStrategy; }
public void execute(){ promotionStrategy.doPromote(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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(); } }
|
策略模式下客户端写法
1 2 3 4 5
| public static void main(String[] args) { String strategy = PromoteStrategyFactory.getPromoteKeySet().stream().findAny().get(); IPromotionStrategy promoteStrategy = PromoteStrategyFactory.createPromoteStrategy(strategy); promoteStrategy.doPromote(); }
|
非策略模式下客户端写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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、本文中的例子虽然简单,但是包含了简单工厂模式,桥接模式,策略模式。任何一种设计模式都难以独立存在,这个大家要注意;