• 《设计模式》之策略模式


    一、什么是策略模式?

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

    1.1策略模式结构图如下

    请添加图片描述

    策略模式需要一个策略接口,不同的策略实现不同的实现类,在具体业务环境中仅持有该策略接口,根据不同的场景使用不同的实现类即可。

    面向接口编程,而不是面向实现。 

    1.2策略模式的优点

    1、干掉繁琐的 if、switch 判断逻辑;

    2、代码优雅、可复用、可读性好;

    3、符合开闭原则(对修改关闭, 对扩展开放),扩展性好、便于维护;

    1.3、策略模式的缺点

    1、策略如果很多的话,会造成策略类膨胀;

    2、使用者必须清楚所有的策略类及其用途;

    二、Jdk 中策略模式的应用

    2.1、线程池中的拒绝策略

    线程池的构造中有一个拒绝策略参数,默认是默认拒绝策略:

    请添加图片描述

    其实这就是一个策略接口

    请添加图片描述

    下面有几种拒绝策略的实现

    请添加图片描述 在创建线程池的时候,就可以传入不同的拒绝策略,这就是 JDK 中策略模式的经典实现了。

    2.2、比较器

    JDK 中大量使用了 Comparator 这个策略接口:

    请添加图片描述

    策略接口有了,但策略需要开发人员自己定。

    集合排序我们比较熟悉的了,不同的排序规则其实就是不同的策略:

    请添加图片描述

    这个策略模式使用了函数式编程接口,比较规则使用匿名内部类或者 Lambda 表达式就搞定了,不需要每个规则定义一个实现类,这样就大量省略策略类了。

    这个策略模式可能藏的比较深,但也是 JDK 中经典的策略模式的应用了。

    三、策略模式代码示例

    3.1、定义支付的策略接口

    1. /**
    2. *
    3. * @author zhongyao
    4. * @date 2019-11-03
    5. * 定义支付的策略接口
    6. */
    7. public interface IPaymentStrategy {
    8. void pay(PaymentContext context);
    9. }

    3.2、策略上下文实现(可以看作是某个业务的实例)

    1. /**
    2. * @author zhongyao
    3. * @date 2019-11-03
    4. * 支付策略的上下文
    5. */
    6. public class PaymentContext {
    7. /**
    8. * 应被支付人员的用户名
    9. */
    10. private String mUserName;
    11. /**
    12. * 应被支付的工资金额
    13. */
    14. private double mMoney;
    15. /**
    16. * 支付方式的策略接口
    17. */
    18. private IPaymentStrategy mIPaymentStrategy;
    19. public PaymentContext(String userName, double money, IPaymentStrategy paymentStrategy) {
    20. mUserName = userName;
    21. mMoney = money;
    22. mIPaymentStrategy = paymentStrategy;
    23. }
    24. public String getUserName() {
    25. return mUserName;
    26. }
    27. public double getMoney() {
    28. return mMoney;
    29. }
    30. public void payNow(){
    31. mIPaymentStrategy.pay(this);
    32. }
    33. }

    3.3、定义具体的策略实现类 

    1. /**
    2. * 人民币现金支付策略实现类
    3. *
    4. * @author zhongyao
    5. * @date 2019-11-03
    6. */
    7. public class RMBCashStrategy implements IPaymentStrategy {
    8. @Override
    9. public void pay(PaymentContext context) {
    10. System.out.println(
    11. "支付方式:人民币现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
    12. + "\n");
    13. }
    14. }
    1. /**
    2. * 美元现金支付策略实现类
    3. *
    4. * @author zhongyao
    5. * @date 2019-11-03
    6. */
    7. public class DollarCashStrategy implements IPaymentStrategy {
    8. @Override
    9. public void pay(PaymentContext context) {
    10. System.out.println(
    11. "支付方式:美元现金支付 " + "收款人---姓名:" + context.getUserName() + " 工资:" + context.getMoney()
    12. + "\n");
    13. }
    14. }

    3.4、客户端调用

    1. IPaymentStrategy rmbCashStrategy = new RMBCashStrategy();
    2. PaymentContext paymentContext1 = new PaymentContext("张三", 6000.0, rmbCashStrategy);
    3. paymentContext1.payNow();
    4. IPaymentStrategy dollarCashStrategy = new DollarCashStrategy();
    5. PaymentContext paymentContext2 = new PaymentContext("Jack", 8000.0, dollarCashStrategy);
    6. paymentContext2.payNow();

    如上,最简单的策略模式使用完毕,就是针对不同的情况,使用不同的策略来完成支付任务。

    3.5、扩展

    当然,如果此时已经用到了很多现金支付的策略,此时支持了银行卡支付,需要扩展一个银行卡支付策略,那么怎么扩展呢,有如下两种方式:

    扩展方式一:扩展上下文的方式

    1、新增一个继承于PaymentContext的子类NewPaymentContext,生成一个新的构造方法,构造参数中包含账户account参数。

    2、新增一个支付策略类,其pay中,需要将PaymentContext转换成所需的NewPaymentContext实例,然后进行后续操作。

    1. import com.hongri.designpattern.strategy.IPaymentStrategy;
    2. import com.hongri.designpattern.strategy.PaymentContext;
    3. /**
    4. * 新扩展的支付策略的上下文
    5. * @author zhongyao
    6. * @date 2019-11-03
    7. */
    8. public class NewPaymentContext extends PaymentContext {
    9. private String mAccount;
    10. /**
    11. * 构造方法,新增了一个账户(account)参数
    12. * @param userName
    13. * @param money
    14. * @param account
    15. * @param paymentStrategy
    16. */
    17. public NewPaymentContext(String userName, double money, String account,
    18. IPaymentStrategy paymentStrategy) {
    19. super(userName, money, paymentStrategy);
    20. mAccount = account;
    21. }
    22. public String getAccount() {
    23. return mAccount;
    24. }
    25. }
    1. import com.hongri.designpattern.strategy.IPaymentStrategy;
    2. import com.hongri.designpattern.strategy.PaymentContext;
    3. /**
    4. * 信用卡支付策略实现类
    5. * @author zhongyao
    6. * @date 2019-11-03
    7. */
    8. public class CreditCardStrategy implements IPaymentStrategy {
    9. /**
    10. * 信用卡策略pay方法中,新增了账号打印
    11. * @param context
    12. */
    13. @Override
    14. public void pay(PaymentContext context) {
    15. //此处需使用新的上下文,需将context转成NewPaymentContext
    16. NewPaymentContext newPaymentContext = (NewPaymentContext)context;
    17. System.out.println(
    18. "支付方式:信用卡支付 " + "收款人---姓名:" + newPaymentContext.getUserName() + " 账号:"
    19. + newPaymentContext.getAccount() + " 工资:" + newPaymentContext.getMoney()
    20. + "\n");
    21. }
    22. }
    1. IPaymentStrategy creditCardStrategy = new CreditCardStrategy();
    2. NewPaymentContext newPaymentContext = new NewPaymentContext("王五", 9000.0, "8384894893",creditCardStrategy);
    3. newPaymentContext.payNow();

    扩展方式二:在策略的算法实现上添加自己需要的数据的方式

    直接新增一个支付策略类,并新增一个全局account变量,当调用支付策略的时候,需要将账户account传递过来。

    1. /**
    2. * 信用卡支付策略实现类2
    3. *
    4. * @author zhongyao
    5. * @date 2019-11-03
    6. */
    7. public class CreditCardStrategy2 implements IPaymentStrategy {
    8. private String mAccount;
    9. public CreditCardStrategy2(String account) {
    10. mAccount = account;
    11. }
    12. public String getAccount() {
    13. return mAccount;
    14. }
    15. @Override
    16. public void pay(PaymentContext context) {
    17. System.out.println(
    18. "支付方式:信用卡支付2 " + "收款人---姓名:" + context.getUserName() + " 账号:"
    19. + getAccount() + " 工资:" + context.getMoney()
    20. + "\n");
    21. }
    22. }

    客户端调用:

    1. IPaymentStrategy creditCardStrategy2 = new CreditCardStrategy2("8384894893");
    2. PaymentContext paymentContext3 = new PaymentContext("王五", 9000.0, creditCardStrategy2);
    3. paymentContext3.payNow();

    对于如上的两种扩展方式,对比如下:

    • 1、对于扩展上下文的方式:这样实现,策略的实现风格更统一,策略需要的数据都统一从上下文来获取,这样在使用方法上也很统一;另外,在上下文中添加新的数据,别的相应算法也可以用得上,可以视为公共的数据。缺点是,这些数据只有一个特定的算法来使用,那么这些数据有些浪费;另外每次添加新的算法都会去扩展上下文,容易形成复杂的上下文对象层次。
    • 2、对于在策略算法的实现上添加自己需要的数据的方式:这样实现,比较简单。缺点是跟其他策略实现的风格不一致,所以外部使用这些策略算法的时候也不一样了,难于以一个统一的方式来动态切换策略算法。 

    可以根据具体的业务需要自行扩展。

    四、何时选用策略模式

    1、出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。

    2、实现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。

    3、需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。

    4、出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

    参考文章: Java策略模式讲解以及示例_如风如云如雨如你的博客-CSDN博客_java策略模式例子

    Java设计模式之策略模式_红日666的博客-CSDN博客_java 策略模式

  • 相关阅读:
    使用 Spring Boot Admin 监控应用状态
    掌动智能:性能压力测试的重要性
    LeetCode每日一题(971. Flip Binary Tree To Match Preorder Traversal)
    超级计算/先进计算的十大用途
    Oracel中视图相关概念和操作(一)
    学生信息登录系统(下)——判断循环语句(五)(for循环篇)
    剑指offer 16 数值的整数次方
    LDA算法并提取这份数据集中各个文档的主题
    机器学习案例(六):加密货币价格预测
    Java泛型的总结
  • 原文地址:https://blog.csdn.net/m0_50370837/article/details/126223439