策略模式
引言
在软件开发中,当同一行为在不同场景需不同实现时(如支付方式选择、排序算法切换),硬编码条件分支会导致代码臃肿和扩展困难。策略模式通过封装算法族并支持运行时替换,实现“行为即插即用”,成为灵活应对变化的利器。
诞生背景
GoF在《设计模式》中提出策略模式,解决三大痛点:
- 算法膨胀:类中充斥大量条件分支(如
if (payType == "alipay") {...}) - 修改封闭性差:新增算法需修改核心类,违反开闭原则
- 复用困难:相似算法无法跨场景复用(如折扣策略在订单/购物车中的差异)
演进过程
- GoF基础(1994):确立核心角色(Context、Strategy、ConcreteStrategy)
- 函数式演进:Lambda表达式使策略模式更简洁(如Java的
Comparator.comparing(...)) - 现代应用:微服务中动态路由策略(如Spring Cloud Gateway的谓词工厂)
核心概念
- 上下文(Context):持有策略引用,通过接口调用算法
- 策略接口(Strategy):定义算法族的统一契约
- 具体策略(ConcreteStrategy):实现策略接口的具体算法
通用实现
Java 实现
java
// 策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 具体策略:支付宝支付
class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
// 具体策略:微信支付
class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 上下文
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(double amount) {
strategy.pay(amount);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
context.setStrategy(new AlipayStrategy());
context.executePayment(100.0); // 支付宝支付:100.0元
context.setStrategy(new WechatPayStrategy());
context.executePayment(200.0); // 微信支付:200.0元
}
}PHP 实现
php
// 策略接口
interface SortStrategy {
public function sort(array $data): array;
}
// 具体策略:快速排序
class QuickSortStrategy implements SortStrategy {
public function sort(array $data): array {
sort($data);
echo "快速排序完成\n";
return $data;
}
}
// 具体策略:冒泡排序
class BubbleSortStrategy implements SortStrategy {
public function sort(array $data): array {
for ($i = 0; $i < count($data); $i++) {
for ($j = 0; $j < count($data) - 1; $j++) {
if ($data[$j] > $data[$j + 1]) {
[$data[$j], $data[$j + 1]] = [$data[$j + 1], $data[$j]];
}
}
}
echo "冒泡排序完成\n";
return $data;
}
}
// 上下文
class SorterContext {
private SortStrategy $strategy;
public function setStrategy(SortStrategy $strategy): void {
$this->strategy = $strategy;
}
public function executeSort(array $data): array {
return $this->strategy->sort($data);
}
}
// 客户端
$context = new SorterContext();
$data = [3, 1, 4, 2];
$context->setStrategy(new QuickSortStrategy());
$context->executeSort($data); // 快速排序完成
$context->setStrategy(new BubbleSortStrategy());
$context->executeSort($data); // 冒泡排序完成应用场景
- 支付方式选择:动态切换支付宝/微信/银行卡
- 排序算法:运行时选择快排/堆排/归并排序
- 折扣策略:会员等级决定不同折扣规则
- 路由导航:根据路况切换最短路径/避开高速策略
案例:电商促销引擎
Java 实现
java
// 策略接口
interface DiscountStrategy {
double applyDiscount(double price);
}
// 具体策略:8折
class TwentyPercentOff implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.8;
}
}
// 具体策略:满减
class FullReduction implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price >= 200 ? price - 50 : price;
}
}
// 上下文
class PromotionEngine {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double calculatePrice(double originalPrice) {
return strategy.applyDiscount(originalPrice);
}
}
// 使用
PromotionEngine engine = new PromotionEngine();
engine.setStrategy(new TwentyPercentOff());
System.out.println(engine.calculatePrice(100)); // 80.0
engine.setStrategy(new FullReduction());
System.out.println(engine.calculatePrice(250)); // 200.0PHP 实现
php
// 策略接口
interface ShippingStrategy {
public function calculateCost(int $weight): int;
}
// 具体策略:顺丰
class SFShipping implements ShippingStrategy {
public function calculateCost(int $weight): int {
return $weight * 8 + 15;
}
}
// 具体策略:邮政
class PostalShipping implements ShippingStrategy {
public function calculateCost(int $weight): int {
return $weight * 5 + 5;
}
}
// 上下文
class ShippingCalculator {
private ShippingStrategy $strategy;
public function setStrategy(ShippingStrategy $strategy): void {
$this->strategy = $strategy;
}
public function getCost(int $weight): int {
return $this->strategy->calculateCost($weight);
}
}
// 使用
$calculator = new ShippingCalculator();
$calculator->setStrategy(new SFShipping());
echo $calculator->getCost(10); // 95
$calculator->setStrategy(new PostalShipping());
echo $calculator->getCost(10); // 55优点
- 开闭原则:新增策略无需修改上下文
- 消除条件分支:避免复杂的
if-else链 - 算法复用:策略可跨多个上下文复用
- 运行时切换:动态改变对象行为
缺点
- 策略类膨胀:大量小类增加维护成本
- 客户端依赖:客户端需知晓不同策略
- 通信开销:策略间共享数据需通过上下文传递
扩展
- 策略工厂:结合工厂模式自动创建策略(如
StrategyFactory.create("alipay")) - 组合策略:多个策略组合使用(如先满减再打折)
- 空对象策略:提供默认空实现避免空指针
模式协作
- 与工厂模式:工厂创建具体策略对象
- 与状态模式:策略关注算法替换,状态关注状态转移
- 与模板方法:策略替换整个算法,模板方法替换算法步骤
延伸思考
- 微服务架构:服务网格中负载均衡策略(如轮询/随机/最少连接)
- 前端领域:React中渲染策略(SSR/CSR动态切换)
- AI决策系统:强化学习中的动作选择策略(ε-贪婪/UCB)
总结
策略模式是行为灵活性的基石,通过解耦算法与上下文,实现“万变不离其宗”的动态扩展。其核心价值在于:算法封装的独立性与运行时切换的自由度。在支付系统、游戏AI、数据处理等场景中,策略模式能显著提升系统适应性和可维护性,成为应对多变需求的利器。