策略模式: 定义一系列的算法, 将其一个个封装起来, 并使它们可相互替换, 使得算法可独立于使用它的客户而变化.
(图片来源: 设计模式: 可复用面向对象软件的基础)
策略模式对应于解决某一问题的一个算法族, 允许用户从该算法族中任选一个算法解决该问题, 同时可以方便的更换算法或者增加新的算法. 并由客户端决定调用哪个算法(核心: 分离算法, 选择实现).
模式实现
案例: 商场打折 -策略可以简单分为: 原价购买、满减、返利三种策略:
Strategy
抽象策略: 定义算法族中所有算法的公共接口, Context使用这个接口来调用ConcreteStrategy定义的算法:
/** * @author jifang * @since 16/8/29 下午7:43. */ public interface Strategy { double acceptCash(double money); }
ConcreteStrategy
具体策略: 以Strategy接口实现某具体算法或行为:
// 正常收费 class Normal implements Strategy { @Override public double acceptCash(double money) { return money; } } // 打折收费 class Discount implements Strategy { private double rate; public Discount(double rate) { if (rate > 1.0) { throw new RuntimeException("折扣力度怎么能大于1.0?"); } this.rate = rate; } @Override public double acceptCash(double money) { return money * rate; } } // 返利收费 class Rebate implements Strategy { private double cashState; private double cashReturn; public Rebate(double cashState, double cashReturn) { this.cashState = cashState; this.cashReturn = cashReturn; } @Override public double acceptCash(double money) { if (money > cashState) { money -= Math.floor(money / cashState) * cashReturn; } return money; } }
Context
上下文:
维护一个Strategy对象的引用;
定义一个接口让Strategy访问它的数据;
public class Context { private Strategy strategy; public void setStrategy(Type type, double... args) { if (type == Type.NORMAL) { strategy = new Normal(); } else if (type == Type.DISCOUNT) { strategy = new Discount(args[0]); } else if (type == Type.REBATE) { strategy = new Rebate(args[0], args[1]); } } public double getResult(double money) { return strategy.acceptCash(money); } public enum Type { NORMAL(0, "正常"), DISCOUNT(1, "打折"), REBATE(2, "返利"); private int value; private String desc; Type(int value, String desc) { this.value = value; this.desc = desc; } } }
注: 将客户端需要选择具体Strategy的任务交给Context完成:
在基础策略模式中,选择所用具体Strategy实现的职责由Client承担, 并将其传递给Context, 这种方案对Client的负担较重, 因此将Context与简单工厂融合, 选择算法实现的工作改由Context负责.
Client
仅与Context交互: 通常有一系列的ConcreteStrategy可供选择.
public class Client { @Test public void client() { double money = 1000; Context context = new Context(); context.setStrategy(Context.Type.NORMAL); System.out.println("原价: [" + context.getResult(money) + "]"); context.setStrategy(Context.Type.REBATE, 100, 20); System.out.println("满100返20: [" + context.getResult(money) + "]"); context.setStrategy(Context.Type.DISCOUNT, 0.8); System.out.println("6折优惠: [" + context.getResult(money) + "]"); } }
小结
作用
析取算法: Strategy接口为Context定义了一个可重用的算法/行为, 继承/实现其有助于析取出算法族的公共功能, 且可减少算法与Client间的耦合.
消除条件语句: 避免将不同行为堆砌在一个类中, 将行为封装在独立的Strategy实现中, 可在Client中消除条件语句.
简化单元测试: 每个算法都有自己的类, 可以单独测试.
场景
当使用一个算法的不同变体, 且这些变体可以实现为一个算法族时;
算法的客户不需要知晓其内部数据, 策略模式可以避免暴露复杂的、与算法相关的数据结构;
一个类定义了多种行为, 且这些行为以多个条件语句形式出现, 可将相关行为各自的Strategy(如: Servlet-api service()方法).
相关模式
Flyweight: Strategy对象经常是很好的轻量级对象.
本文分享自微信公众号 - Java帮帮(javahelp)
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2017-01-04
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句