文章目录
- 一、概述
-
- [1.1 结构与角色](#1.1 结构与角色)
- [1.2 适用场景](#1.2 适用场景)
- 二、实现方式
-
- [2.1 基础实现](#2.1 基础实现)
- [2.2 运行时动态切换](#2.2 运行时动态切换)
- [2.3 策略模式 vs 简单 if-else 对比](#2.3 策略模式 vs 简单 if-else 对比)
- [三、JDK 源码中的策略模式](#三、JDK 源码中的策略模式)
-
- [3.1 Comparator------排序策略](#3.1 Comparator——排序策略)
- [3.2 ThreadPoolExecutor 的拒绝策略](#3.2 ThreadPoolExecutor 的拒绝策略)
- [3.3 Arrays.sort() 的排序策略选择](#3.3 Arrays.sort() 的排序策略选择)
- [四、策略模式消除 if-else 实战](#四、策略模式消除 if-else 实战)
-
- [4.1 问题场景------促销折扣系统](#4.1 问题场景——促销折扣系统)
- [4.2 策略模式重构](#4.2 策略模式重构)
- [4.3 Spring 容器自动注册策略](#4.3 Spring 容器自动注册策略)
- [4.4 三种消除 if-else 方式对比](#4.4 三种消除 if-else 方式对比)
- 五、总结
一、概述
在软件开发中,经常会遇到这样的场景:实现某个功能有多种方式(算法),比如支付系统支持微信、支付宝、银行卡等多种支付方式;出行导航支持最短时间、最短距离、最少收费等多种路线策略;促销活动支持满减、折扣、买赠等多种优惠方案。如果将这些不同的实现方式全部写在一个类中,使用大量的 if-else 或 switch-case 来区分,会导致代码臃肿、难以维护、扩展困难:
if type == A
else if type == B
else if type == C
else if type == D
... 更多分支
客户端代码
策略A实现
策略B实现
策略C实现
策略D实现
策略N实现
每新增一种策略,就要修改客户端代码,违反了开闭原则。
策略模式(Strategy Pattern)正是为了解决这个问题而诞生的------它定义一系列算法,将每一个算法封装起来,并使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
生活中的策略模式例子:
- 出行方式:去机场可以坐地铁、打车、坐公交、骑车,每种方式都是一种出行策略,根据时间和费用灵活选择
- 促销活动:双十一的优惠策略有满减、打折、买赠、秒杀,不同商品可以配置不同的促销策略
- 排序算法:对不同规模的数据可以选择冒泡排序、快速排序、归并排序等不同策略
- 压缩工具:支持 ZIP、RAR、7Z 等多种压缩策略,用户按需选择
核心:定义一系列算法,将每一个算法封装起来,并使它们可以互相替换,让算法的变化独立于使用算法的客户端
1.1 结构与角色
策略模式包含以下角色:
持有
实现
实现
实现
委托调用
Client 客户端
Context 上下文
Strategy 策略接口
ConcreteStrategyA 具体策略A
ConcreteStrategyB 具体策略B
ConcreteStrategyC 具体策略C
- Strategy(策略接口):定义所有支持的算法的公共接口,Context 使用这个接口来调用某个具体策略定义的算法
- ConcreteStrategy(具体策略):实现策略接口的具体算法
- Context(上下文):持有策略接口的引用,将客户端的请求委托给策略对象执行,可以在运行时动态切换策略
- Client(客户端):创建具体策略对象和上下文对象,并将策略对象注入上下文
1.2 适用场景
- 系统中需要动态地在几种算法中选择一种,避免使用多重条件判断
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现
- 不希望客户端知道复杂的、与算法相关的数据结构,想将算法和算法相关的数据封装起来
- 多个类只在算法或行为上稍有不同的场景
二、实现方式
策略模式的核心实现思路是:将不同的算法分别封装为独立的策略类,它们实现同一个策略接口,上下文类通过组合持有策略接口的引用,将算法的执行委托给策略对象。
2.1 基础实现
以"支付系统"为例,支持微信支付、支付宝支付、银行卡支付三种支付策略:
持有
实现
实现
实现
委托调用
客户端
PaymentContext 支付上下文
PaymentStrategy 策略接口
WechatPay 微信支付
AliPay 支付宝支付
BankPay 银行卡支付
(1)策略接口------支付策略
java
/**
* 策略接口:支付策略
*/
public interface PaymentStrategy {
/**
* 支付
*
* @param amount 支付金额
*/
void pay(double amount);
}
(2)具体策略------微信支付、支付宝支付、银行卡支付
java
/**
* 具体策略:微信支付
*/
public class WechatPay implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + " 元");
}
}
/**
* 具体策略:支付宝支付
*/
public class AliPay implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + " 元");
}
}
/**
* 具体策略:银行卡支付
*/
public class BankPay implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("银行卡支付:" + amount + " 元");
}
}
(3)上下文------支付上下文
java
/**
* 上下文:支付上下文
* 持有 PaymentStrategy 的引用,将支付操作委托给策略对象
*/
public class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
/**
* 设置策略(支持运行时动态切换)
*
* @param strategy 支付策略
*/
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
/**
* 执行支付
*
* @param amount 支付金额
*/
public void executePay(double amount) {
strategy.pay(amount);
}
}
(4)客户端调用
java
public class StrategyDemo {
public static void main(String[] args) {
// 创建上下文,指定微信支付策略
PaymentContext context = new PaymentContext(new WechatPay());
context.executePay(100.0);
// 微信支付:100.0 元
// 动态切换为支付宝支付
context.setStrategy(new AliPay());
context.executePay(200.0);
// 支付宝支付:200.0 元
// 动态切换为银行卡支付
context.setStrategy(new BankPay());
context.executePay(300.0);
// 银行卡支付:300.0 元
}
}
关键点 :上下文
PaymentContext通过组合持有PaymentStrategy接口的引用,将支付操作委托给策略对象。新增支付方式只需要新增一个实现类,无需修改上下文类和已有的策略类,符合开闭原则。
2.2 运行时动态切换
策略模式的一大优势是支持运行时动态切换策略。以"出行方式"为例,根据不同场景选择不同的出行策略:
(1)策略接口------出行策略
java
/**
* 策略接口:出行策略
*/
public interface TravelStrategy {
/**
* 出行
*
* @param distance 距离(公里)
*/
void travel(double distance);
}
(2)具体策略
java
/**
* 具体策略:地铁出行
*/
public class SubwayStrategy implements TravelStrategy {
@Override
public void travel(double distance) {
System.out.println("乘坐地铁出行 " + distance + " 公里,费用约 " + (3 + distance * 0.5) + " 元");
}
}
/**
* 具体策略:打车出行
*/
public class TaxiStrategy implements TravelStrategy {
@Override
public void travel(double distance) {
System.out.println("打车出行 " + distance + " 公里,费用约 " + (13 + distance * 2.3) + " 元");
}
}
/**
* 具体策略:骑行出行
*/
public class BikeStrategy implements TravelStrategy {
@Override
public void travel(double distance) {
System.out.println("骑行出行 " + distance + " 公里,费用约 1.5 元");
}
}
(3)上下文------出行导航
java
/**
* 上下文:出行导航
* 根据用户选择的策略计算出行方案
*/
public class TravelNavigator {
private TravelStrategy strategy;
public TravelNavigator(TravelStrategy strategy) {
this.strategy = strategy;
}
/**
* 设置出行策略
*
* @param strategy 出行策略
*/
public void setStrategy(TravelStrategy strategy) {
this.strategy = strategy;
}
/**
* 计算出行方案
*
* @param distance 距离(公里)
*/
public void navigate(double distance) {
System.out.print("【出行方案】");
strategy.travel(distance);
}
}
(4)客户端调用------运行时动态切换
java
public class TravelDemo {
public static void main(String[] args) {
TravelNavigator navigator = new TravelNavigator(new SubwayStrategy());
// 3 公里以内------骑行
double distance = 2.5;
if (distance <= 3) {
navigator.setStrategy(new BikeStrategy());
} else if (distance <= 20) {
navigator.setStrategy(new SubwayStrategy());
} else {
navigator.setStrategy(new TaxiStrategy());
}
navigator.navigate(distance);
// 【出行方案】骑行出行 2.5 公里,费用约 1.5 元
// 20 公里------打车
distance = 25;
if (distance <= 3) {
navigator.setStrategy(new BikeStrategy());
} else if (distance <= 20) {
navigator.setStrategy(new SubwayStrategy());
} else {
navigator.setStrategy(new TaxiStrategy());
}
navigator.navigate(distance);
// 【出行方案】打车出行 25.0 公里,费用约 70.5 元
}
}
注意 :上面的客户端代码中仍然有
if-else逻辑来选择策略。这部分逻辑可以进一步优化------参见第四章"策略模式消除 if-else 实战"。
2.3 策略模式 vs 简单 if-else 对比
以 3 种支付方式为例:
| 对比维度 | if-else 实现 | 策略模式 |
|---|---|---|
| 代码结构 | 所有逻辑集中在一个方法中 | 每种策略独立封装 |
| 新增策略 | 修改已有代码,加 else-if | 新增一个策略类,无需修改已有代码 |
| 代码可读性 | 分支越多越难读 | 每个策略类职责单一,清晰明了 |
| 可维护性 | 牵一发动全身 | 策略间互不影响 |
| 符合开闭原则 | 否 | 是 |
| 运行时切换 | 不支持 | 支持动态切换 |
| 类的数量 | 少 | 略多(每种策略一个类) |
三、JDK 源码中的策略模式
策略模式在 JDK 中有着广泛的应用,下面来看几个经典的例子。
3.1 Comparator------排序策略
java.util.Comparator 是策略模式最经典的体现。不同的 Comparator 实现就是不同的排序策略,可以灵活地改变排序规则而无需修改被排序的对象。
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparatorDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Charlie");
names.add("Alice");
names.add("Bob");
// 策略一:自然排序(字典序)
Collections.sort(names, Comparator.naturalOrder());
System.out.println("自然排序:" + names);
// 自然排序:[Alice, Bob, Charlie]
// 策略二:按字符串长度排序
Collections.sort(names, Comparator.comparingInt(String::length));
System.out.println("长度排序:" + names);
// 长度排序:[Bob, Alice, Charlie]
// 策略三:逆序排序
Collections.sort(names, Comparator.reverseOrder());
System.out.println("逆序排序:" + names);
// 逆序排序:[Charlie, Bob, Alice]
}
}
在这个例子中:
- Strategy(策略接口) :
Comparator<T> - ConcreteStrategy(具体策略) :各种 Comparator 实现(
naturalOrder()、comparingInt()、reverseOrder()等) - Context(上下文) :
Collections.sort()/List.sort(),接受 Comparator 作为排序策略
关键点 :
Collections.sort()方法不需要知道具体的排序策略是什么,它只依赖Comparator接口。客户端可以随时传入不同的 Comparator 来改变排序行为,这就是策略模式的精髓。
3.2 ThreadPoolExecutor 的拒绝策略
java.util.concurrent.ThreadPoolExecutor 中的拒绝策略(RejectedExecutionHandler)也是策略模式的典型应用。当线程池的工作队列已满且线程数达到最大值时,新提交的任务需要按照某种策略来处理,JDK 提供了四种内置策略:
java
/**
* 拒绝策略接口 ------ Strategy
*/
public interface RejectedExecutionHandler {
/**
* 拒绝任务时的处理方法
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
JDK 内置了四种具体策略:
| 策略 | 类名 | 行为 |
|---|---|---|
| AbortPolicy | ThreadPoolExecutor.AbortPolicy |
直接抛出 RejectedExecutionException(默认策略) |
| CallerRunsPolicy | ThreadPoolExecutor.CallerRunsPolicy |
由提交任务的线程自己执行该任务 |
| DiscardPolicy | ThreadPoolExecutor.DiscardPolicy |
直接丢弃任务,不抛出异常 |
| DiscardOldestPolicy | ThreadPoolExecutor.DiscardOldestPolicy |
丢弃队列中最旧的任务,然后重新提交当前任务 |
使用方式:
java
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class RejectedPolicyDemo {
public static void main(String[] args) {
// 创建线程池,指定拒绝策略为 CallerRunsPolicy
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
2, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(1), // 工作队列容量为 1
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 也可以在运行时动态切换拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
}
}
在这个例子中:
- Strategy(策略接口) :
RejectedExecutionHandler - ConcreteStrategy(具体策略) :
AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy - Context(上下文) :
ThreadPoolExecutor,持有拒绝策略的引用
关键点 :线程池通过
setRejectedExecutionHandler()方法支持运行时动态切换拒绝策略,这正是策略模式的典型特征。
3.3 Arrays.sort() 的排序策略选择
JDK 中 Arrays.sort() 方法根据数组类型和大小自动选择不同的排序算法,这也是策略模式的思想体现:
| 数组类型 | 排序策略 | 说明 |
|---|---|---|
| 基本类型数组 | 双轴快排(Dual-Pivot Quicksort) | 适合基本类型,不要求稳定性 |
| 对象数组 | TimSort(归并+插入混合排序) | 保证稳定性,适合对象比较 |
| 小数组 | 插入排序 | 元素少于 47 时切换为插入排序 |
java
import java.util.Arrays;
import java.util.Comparator;
public class ArraysSortDemo {
public static void main(String[] args) {
// 基本类型------使用双轴快排
int[] nums = {5, 2, 8, 1, 9};
Arrays.sort(nums);
System.out.println("基本类型排序:" + Arrays.toString(nums));
// 基本类型排序:[1, 2, 5, 8, 9]
// 对象类型------使用 TimSort
String[] names = {"Charlie", "Alice", "Bob"};
Arrays.sort(names);
System.out.println("对象类型排序:" + Arrays.toString(names));
// 对象类型排序:[Alice, Bob, Charlie]
// 自定义排序策略------按长度排序
Arrays.sort(names, Comparator.comparingInt(String::length));
System.out.println("自定义策略排序:" + Arrays.toString(names));
// 自定义策略排序:[Bob, Alice, Charlie]
}
}
四、策略模式消除 if-else 实战
策略模式最实用的场景之一就是消除代码中大量的 if-else 或 switch-case 分支。下面通过一个完整的实战案例,展示如何从 if-else 重构为策略模式。
4.1 问题场景------促销折扣系统
假设有一个电商系统的促销模块,支持多种优惠策略:
java
/**
* 促销服务------if-else 实现
*/
public class PromotionServiceBad {
/**
* 计算折扣价格
*
* @param type 促销类型
* @param price 原价
* @param param 促销参数(如折扣率、满减门槛等)
* @return 折后价
*/
public double calculatePrice(String type, double price, double param) {
if ("DISCOUNT".equals(type)) {
// 打折策略
return price * param;
} else if ("FULL_REDUCTION".equals(type)) {
// 满减策略
return price >= param ? price - 20 : price;
} else if ("BUY_ONE_GET_ONE_FREE".equals(type)) {
// 买一赠一策略
return price * 0.5;
} else if ("CASHBACK".equals(type)) {
// 返现策略
return price - param;
} else {
throw new IllegalArgumentException("不支持的促销类型:" + type);
}
}
}
问题 :每新增一种促销策略,就要修改 calculatePrice 方法,违反开闭原则,且 if-else 分支会越来越多,可读性越来越差。
4.2 策略模式重构
(1)策略接口
java
/**
* 策略接口:促销策略
*/
public interface PromotionStrategy {
/**
* 计算折后价格
*
* @param price 原价
* @param param 促销参数
* @return 折后价
*/
double calculatePrice(double price, double param);
}
(2)具体策略
java
/**
* 具体策略:打折
*/
public class DiscountStrategy implements PromotionStrategy {
@Override
public double calculatePrice(double price, double param) {
return price * param;
}
}
/**
* 具体策略:满减
*/
public class FullReductionStrategy implements PromotionStrategy {
@Override
public double calculatePrice(double price, double param) {
return price >= param ? price - 20 : price;
}
}
/**
* 具体策略:买一赠一
*/
public class BuyOneGetOneFreeStrategy implements PromotionStrategy {
@Override
public double calculatePrice(double price, double param) {
return price * 0.5;
}
}
/**
* 具体策略:返现
*/
public class CashbackStrategy implements PromotionStrategy {
@Override
public double calculatePrice(double price, double param) {
return price - param;
}
}
(3)策略工厂------消除 if-else 的关键
java
import java.util.HashMap;
import java.util.Map;
/**
* 策略工厂:通过 Map 注册策略,根据类型获取对应策略
* 消除 if-else 的关键:用 Map 查找代替条件判断
*/
public class PromotionStrategyFactory {
private static final Map<String, PromotionStrategy> STRATEGY_MAP = new HashMap<>();
static {
// 注册策略
STRATEGY_MAP.put("DISCOUNT", new DiscountStrategy());
STRATEGY_MAP.put("FULL_REDUCTION", new FullReductionStrategy());
STRATEGY_MAP.put("BUY_ONE_GET_ONE_FREE", new BuyOneGetOneFreeStrategy());
STRATEGY_MAP.put("CASHBACK", new CashbackStrategy());
}
/**
* 获取策略
*
* @param type 促销类型
* @return 对应的促销策略
*/
public static PromotionStrategy getStrategy(String type) {
PromotionStrategy strategy = STRATEGY_MAP.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的促销类型:" + type);
}
return strategy;
}
/**
* 注册新策略(支持动态扩展)
*
* @param type 促销类型
* @param strategy 促销策略
*/
public static void registerStrategy(String type, PromotionStrategy strategy) {
STRATEGY_MAP.put(type, strategy);
}
}
(4)上下文
java
/**
* 上下文:促销上下文
*/
public class PromotionContext {
private final PromotionStrategy strategy;
public PromotionContext(String type) {
this.strategy = PromotionStrategyFactory.getStrategy(type);
}
/**
* 计算折后价格
*
* @param price 原价
* @param param 促销参数
* @return 折后价
*/
public double calculatePrice(double price, double param) {
return strategy.calculatePrice(price, param);
}
}
(5)客户端调用
java
public class PromotionDemo {
public static void main(String[] args) {
// 打折策略------8折
PromotionContext discount = new PromotionContext("DISCOUNT");
System.out.println("打折后价格:" + discount.calculatePrice(100, 0.8));
// 打折后价格:80.0
// 满减策略------满200减20
PromotionContext fullReduction = new PromotionContext("FULL_REDUCTION");
System.out.println("满减后价格:" + fullReduction.calculatePrice(250, 200));
// 满减后价格:230.0
// 买一赠一策略
PromotionContext buyOneGetOne = new PromotionContext("BUY_ONE_GET_ONE_FREE");
System.out.println("买一赠一价格:" + buyOneGetOne.calculatePrice(100, 0));
// 买一赠一价格:50.0
// 返现策略------返现30
PromotionContext cashback = new PromotionContext("CASHBACK");
System.out.println("返现后价格:" + cashback.calculatePrice(100, 30));
// 返现后价格:70.0
}
}
核心思路 :用
Map<String, Strategy>代替if-else,通过策略类型从 Map 中查找对应的策略对象,新增策略只需注册到 Map 中,无需修改任何已有代码。
4.3 Spring 容器自动注册策略
在 Spring 项目中,可以借助依赖注入自动完成策略注册,进一步简化代码:
(1)策略接口添加类型标识
java
/**
* 策略接口:促销策略
* 增加 getType() 方法用于标识策略类型
*/
public interface PromotionStrategy {
/**
* 获取策略类型
*
* @return 策略类型标识
*/
String getType();
/**
* 计算折后价格
*
* @param price 原价
* @param param 促销参数
* @return 折后价
*/
double calculatePrice(double price, double param);
}
(2)具体策略加上 @Component 注解
java
import org.springframework.stereotype.Component;
@Component
public class DiscountStrategy implements PromotionStrategy {
@Override
public String getType() {
return "DISCOUNT";
}
@Override
public double calculatePrice(double price, double param) {
return price * param;
}
}
@Component
public class FullReductionStrategy implements PromotionStrategy {
@Override
public String getType() {
return "FULL_REDUCTION";
}
@Override
public double calculatePrice(double price, double param) {
return price >= param ? price - 20 : price;
}
}
@Component
public class CashbackStrategy implements PromotionStrategy {
@Override
public String getType() {
return "CASHBACK";
}
@Override
public double calculatePrice(double price, double param) {
return price - param;
}
}
(3)Spring 自动注册策略工厂
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 策略工厂:Spring 自动注册
* 通过 Spring 依赖注入自动收集所有 PromotionStrategy 实现
* 无需手动注册,新增策略只需添加 @Component 注解即可
*/
@Component
public class SpringPromotionStrategyFactory {
@Autowired
private List<PromotionStrategy> strategyList;
private final Map<String, PromotionStrategy> strategyMap = new HashMap<>();
@PostConstruct
public void init() {
// Spring 自动注入所有 PromotionStrategy 实现
// 遍历注册到 Map 中
for (PromotionStrategy strategy : strategyList) {
strategyMap.put(strategy.getType(), strategy);
}
}
/**
* 获取策略
*
* @param type 促销类型
* @return 对应的促销策略
*/
public PromotionStrategy getStrategy(String type) {
PromotionStrategy strategy = strategyMap.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的促销类型:" + type);
}
return strategy;
}
}
优势 :新增策略时,只需创建一个实现
PromotionStrategy接口的类并添加@Component注解,Spring 会自动发现并注册,完全无需修改任何已有代码,真正实现了开闭原则。
4.4 三种消除 if-else 方式对比
| 方式 | if-else 分支 | 新增策略 | 依赖 | 适用场景 |
|---|---|---|---|---|
| if-else 原始实现 | 多 | 修改已有代码 | 无 | 策略极少且固定 |
| Map 注册策略工厂 | 无 | 新增策略类 + 注册到 Map | 无 | 普通 Java 项目 |
| Spring 自动注册 | 无 | 新增策略类 + @Component | Spring | Spring 项目 |
五、总结
策略模式的核心思想是定义一系列算法,将每一个算法封装起来,并使它们可以互相替换,让算法的变化独立于使用算法的客户端。
优点:
- 消除 if-else:用策略类代替多重条件判断,代码更清晰
- 符合开闭原则:新增策略只需新增一个策略类,无需修改已有代码
- 算法可自由切换:上下文通过组合持有策略引用,支持运行时动态切换
- 策略可复用:同一策略可在不同上下文中复用
- 避免多重条件判断:将分支逻辑分散到各个策略类中,每个类职责单一
缺点:
- 增加类的数量:每种策略都需要一个独立的类,策略越多类越多
- 客户端必须了解所有策略:客户端需要知道有哪些策略可用,才能选择合适的策略
- 策略对象可能无法复用:某些策略只在特定场景下使用,可能导致策略类"膨胀"
适用场景:
- 系统中需要动态地在几种算法中选择一种
- 需要避免使用多重条件判断(if-else / switch-case)的场景
- 一个类定义了多种行为,且这些行为以条件语句的形式出现
- 多个类只在算法或行为上有所不同
策略模式 vs 状态模式 :策略模式和状态模式结构非常相似,都是通过组合持有接口引用来委托行为,但意图不同------策略模式的客户端主动选择 策略,策略之间是平等的替换关系;状态模式的状态由对象内部自动切换,状态之间有流转逻辑。
参考博客:
策略模式 | 菜鸟教程:https://www.runoob.com/design-pattern/strategy-pattern.html