Skip to content

策略模式

引言

在软件开发中,当同一行为在不同场景需不同实现时(如支付方式选择、排序算法切换),硬编码条件分支会导致代码臃肿扩展困难。策略模式通过封装算法族并支持运行时替换,实现“行为即插即用”,成为灵活应对变化的利器。

诞生背景

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.0

PHP 实现

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
  • 算法复用:策略可跨多个上下文复用
  • 运行时切换:动态改变对象行为

缺点

  1. 策略类膨胀:大量小类增加维护成本
  2. 客户端依赖:客户端需知晓不同策略
  3. 通信开销:策略间共享数据需通过上下文传递

扩展

  • 策略工厂:结合工厂模式自动创建策略(如StrategyFactory.create("alipay")
  • 组合策略:多个策略组合使用(如先满减再打折)
  • 空对象策略:提供默认空实现避免空指针

模式协作

  • 与工厂模式:工厂创建具体策略对象
  • 与状态模式:策略关注算法替换,状态关注状态转移
  • 与模板方法:策略替换整个算法,模板方法替换算法步骤

延伸思考

  • 微服务架构:服务网格中负载均衡策略(如轮询/随机/最少连接)
  • 前端领域:React中渲染策略(SSR/CSR动态切换)
  • AI决策系统:强化学习中的动作选择策略(ε-贪婪/UCB)

总结

策略模式是行为灵活性的基石,通过解耦算法与上下文,实现“万变不离其宗”的动态扩展。其核心价值在于:算法封装的独立性运行时切换的自由度。在支付系统、游戏AI、数据处理等场景中,策略模式能显著提升系统适应性和可维护性,成为应对多变需求的利器。