您现在的位置是:首页 >学无止境 >《设计模式》策略模式网站首页学无止境

《设计模式》策略模式

ReadThroughLife 2023-06-27 00:00:03
简介《设计模式》策略模式

《设计模式》策略模式

定义

  • 又叫作政策模式,将定义的一系列算法封装起来,使得它们之间可以互相替换,从而让算法的变化不影响到使用算法的用户
  • 属于行为型模式。

策略模式的组成角色

  • 环境类(Context)环境类是使用算法的角色,用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略、算法的直接访问,封装可能存在的变化。
  • 抽象策略(Strategy)规定策略或算法的行为,为所支持的算法声明了抽象方法,是所有具体策略类的父类。
  • 具体策略类(ConcreteStrategy)实现了在抽象策略类中声明的算法。在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

策略模式的 UML 类图
在这里插入图片描述

🎈情景案例:某数码产品销售店家推送促销优惠活动,推出三种优惠措施:1、店铺商品购买金额满1000元减100元。2、店铺商品购买金额满3000元加购100元送一年保修服务。3、店铺商品金额满5000元打9.5折。

针对以上情景案例使用策略模式设计一个方案:上面三种优惠措施相当于三种具体的策略,记作 Discount1、Discount2 以及 Discount3,它们都实现了共同的接口 Discount,设计的 UML 图如下:

在这里插入图片描述
Discount 接口

public interface Discount {
    void discount();
}

Discount1 类

public class Discount1 implements Discount {
    @Override
    public void discount() {
        System.out.println("店铺商品购买金额满1000元减100元");
    }
}

Discount2 类

public class Discount2 implements Discount {
    @Override
    public void discount() {
        System.out.println("店铺商品购买金额满3000元加购100元送一年保修服务");
    }
}

Discount3 类

public class Discount3 implements Discount {
    @Override
    public void discount() {
        System.out.println("店铺商品金额满5000元打9.5折");
    }
}

Store 类

public class Store {
    private Discount discount;

    public Store(Discount discount) {
        this.discount = discount;
    }

    public void sale() {
        discount.discount();
    }
}

策略模式的优点

  • 符合开闭原则。用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 便于复用。由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
  • 避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。

策略模式的缺点

  • 增加维护难度。策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
  • 只适用于客户端知道所有的算法或行为的情况。客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 无法同时在客户端使用多个策略类。客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

策略模式的使用场景

  • 对多个算法进行封装。一个系统需要动态地在几种算法中选择一种,可以将这些算法封装到一个个的具体算法类中。
  • 想要屏蔽算法数据结构,提高保密性与安全性。不希望客户端知道复杂的、与算法相关的数据结构。在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。
  • 不想使用多重条件选择语句。一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。策略模式将行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。

🎈策略模式在 JDK 源码中的应用

java.util.Comparator 接口就是一个抽象策略,java.util.Arrays 类就是一个环境类,在调用Arrays.sort(T[] a, Comparator<? super T> c)方法时需要传入具体策略,Arrays 环境类根据具体策略进行排序,该过程的调用链路涉及的代码如下所示:

客户端调用示例如下

Integer[] a = {1, 2, 3, 4, 5};
Arrays.sort(a, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});
System.out.println(Arrays.toString(a));

Arrays.sort(T[] a, Comparator<? super T> c) 源码

public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

TimSort 类中 sort 和 countRunAndMakeAscending 方法源码

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                      T[] work, int workBase, int workLen) {
     assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

     int nRemaining  = hi - lo;
     if (nRemaining < 2)
         return;  // Arrays of size 0 and 1 are always sorted

     // If array is small, do a "mini-TimSort" with no merges
     if (nRemaining < MIN_MERGE) {
         int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
         binarySort(a, lo, hi, lo + initRunLen, c);
         return;
     }
     // ...
 }
 private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
                                                    Comparator<? super T> c) {
     assert lo < hi;
     int runHi = lo + 1;
     if (runHi == hi)
         return 1;

     // Find end of run, and reverse range if descending
     if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
         while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
             runHi++;
         reverseRange(a, lo, runHi);
     } else {                              // Ascending
         while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
             runHi++;
     }

     return runHi - lo;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。