比如对象的某个行为,在不同场景有不同实现方式,可以将这些行为的具体实现定义为一组策略,每个实现类实现种策略,在不同场景使用不同的实现,并且可以自由切换策略。

策略模式需要一个策略接口,不同的策略实现不同的实现类,在具体业务环境中仅持有该策略接口,根据不同的场景使用不同的实现类即可。
面向接口编程,而不是面向实现。
1、干掉繁琐的 if、switch 判断逻辑;
2、代码优雅、可复用、可读性好;
3、符合开闭原则(对修改关闭, 对扩展开放),扩展性好、便于维护;
1、策略如果很多的话,会造成策略类膨胀;
2、使用者必须清楚所有的策略类及其用途;
线程池的构造中有一个拒绝策略参数,默认是默认拒绝策略:

其实这就是一个策略接口:

下面有几种拒绝策略的实现:
在创建线程池的时候,就可以传入不同的拒绝策略,这就是 JDK 中策略模式的经典实现了。
JDK 中大量使用了 Comparator 这个策略接口:

策略接口有了,但策略需要开发人员自己定。
集合排序我们比较熟悉的了,不同的排序规则其实就是不同的策略:

这个策略模式使用了函数式编程接口,比较规则使用匿名内部类或者 Lambda 表达式就搞定了,不需要每个规则定义一个实现类,这样就大量省略策略类了。
这个策略模式可能藏的比较深,但也是 JDK 中经典的策略模式的应用了。
-
- /**
- *
- * @author zhongyao
- * @date 2019-11-03
- * 定义支付的策略接口
- */
- public interface IPaymentStrategy {
-
- void pay(PaymentContext context);
- }
- /**
- * @author zhongyao
- * @date 2019-11-03
- * 支付策略的上下文
- */
- public class PaymentContext {
-
- /**
- * 应被支付人员的用户名
- */
- private String mUserName;
-
- /**
- * 应被支付的工资金额
- */
- private double mMoney;
-
- /**
- * 支付方式的策略接口
- */
- private IPaymentStrategy mIPaymentStrategy;
-
- public PaymentContext(String userName, double money, IPaymentStrategy paymentStrategy) {
- mUserName = userName;
- mMoney = money;
- mIPaymentStrategy = paymentStrategy;
- }
-
-
- public String getUserName() {
- return mUserName;
- }
-
- public double getMoney() {
- return mMoney;
- }
-
- public void payNow(){
- mIPaymentStrategy.pay(this);
- }
-
- }
- /**
- * 人民币现金支付策略实现类
- *
- * @author zhongyao
- * @date 2019-11-03
- */
- public class RMBCashStrategy implements IPaymentStrategy {
- @Override
- public void pay(PaymentContext context) {
- System.out.println(
- "支付方式:人民币现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
- + "\n");
- }
- }
- /**
- * 美元现金支付策略实现类
- *
- * @author zhongyao
- * @date 2019-11-03
- */
- public class DollarCashStrategy implements IPaymentStrategy {
- @Override
- public void pay(PaymentContext context) {
- System.out.println(
- "支付方式:美元现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
- + "\n");
- }
- }
- IPaymentStrategy rmbCashStrategy = new RMBCashStrategy();
- PaymentContext paymentContext1 = new PaymentContext("张三", 6000.0, rmbCashStrategy);
- paymentContext1.payNow();
-
- IPaymentStrategy dollarCashStrategy = new DollarCashStrategy();
- PaymentContext paymentContext2 = new PaymentContext("Jack", 8000.0, dollarCashStrategy);
- paymentContext2.payNow();
如上,最简单的策略模式使用完毕,就是针对不同的情况,使用不同的策略来完成支付任务。
当然,如果此时已经用到了很多现金支付的策略,此时支持了银行卡支付,需要扩展一个银行卡支付策略,那么怎么扩展呢,有如下两种方式:
1、新增一个继承于PaymentContext的子类NewPaymentContext,生成一个新的构造方法,构造参数中包含账户account参数。
2、新增一个支付策略类,其pay中,需要将PaymentContext转换成所需的NewPaymentContext实例,然后进行后续操作。
- import com.hongri.designpattern.strategy.IPaymentStrategy;
- import com.hongri.designpattern.strategy.PaymentContext;
-
- /**
- * 新扩展的支付策略的上下文
- * @author zhongyao
- * @date 2019-11-03
- */
- public class NewPaymentContext extends PaymentContext {
- private String mAccount;
-
- /**
- * 构造方法,新增了一个账户(account)参数
- * @param userName
- * @param money
- * @param account
- * @param paymentStrategy
- */
- public NewPaymentContext(String userName, double money, String account,
- IPaymentStrategy paymentStrategy) {
- super(userName, money, paymentStrategy);
- mAccount = account;
- }
-
- public String getAccount() {
- return mAccount;
- }
- }
- import com.hongri.designpattern.strategy.IPaymentStrategy;
- import com.hongri.designpattern.strategy.PaymentContext;
-
- /**
- * 信用卡支付策略实现类
- * @author zhongyao
- * @date 2019-11-03
- */
- public class CreditCardStrategy implements IPaymentStrategy {
- /**
- * 信用卡策略pay方法中,新增了账号打印
- * @param context
- */
- @Override
- public void pay(PaymentContext context) {
- //此处需使用新的上下文,需将context转成NewPaymentContext
- NewPaymentContext newPaymentContext = (NewPaymentContext)context;
- System.out.println(
- "支付方式:信用卡支付 " + "收款人---姓名:" + newPaymentContext.getUserName() + " 账号:"
- + newPaymentContext.getAccount() + " 工资:" + newPaymentContext.getMoney()
- + "\n");
- }
- }
- IPaymentStrategy creditCardStrategy = new CreditCardStrategy();
- NewPaymentContext newPaymentContext = new NewPaymentContext("王五", 9000.0, "8384894893",creditCardStrategy);
- newPaymentContext.payNow();
直接新增一个支付策略类,并新增一个全局account变量,当调用支付策略的时候,需要将账户account传递过来。
- /**
- * 信用卡支付策略实现类2
- *
- * @author zhongyao
- * @date 2019-11-03
- */
- public class CreditCardStrategy2 implements IPaymentStrategy {
-
- private String mAccount;
-
- public CreditCardStrategy2(String account) {
- mAccount = account;
- }
-
- public String getAccount() {
- return mAccount;
- }
-
- @Override
- public void pay(PaymentContext context) {
- System.out.println(
- "支付方式:信用卡支付2 " + "收款人---姓名:" + context.getUserName() + " 账号:"
- + getAccount() + " 工资:" + context.getMoney()
- + "\n");
- }
- }
客户端调用:
- IPaymentStrategy creditCardStrategy2 = new CreditCardStrategy2("8384894893");
- PaymentContext paymentContext3 = new PaymentContext("王五", 9000.0, creditCardStrategy2);
- paymentContext3.payNow();
对于如上的两种扩展方式,对比如下:
可以根据具体的业务需要自行扩展。
1、出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。
2、实现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
3、需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。
4、出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。