DEV Community

架构师小白
架构师小白

Posted on

策略模式深度指南:封装可替换算法的艺术

策略模式深度指南:封装可替换算法的艺术

策略模式(Strategy Pattern)是一种行为设计模式,它让你能在运行时选择算法的行为。就像在餐厅点餐时,可以从菜单中选择不同的烹饪方式——可以清蒸、红烧或油炸,每种方式都封装了一种具体的算法。

为什么要使用策略模式?

在软件开发中,我们经常遇到需要根据不同情况使用不同算法的场景:

  • 不同的支付方式(支付宝、微信、银行卡)
  • 不同的排序算法(快速排序、归并排序、堆排序)
  • 不同的压缩算法(ZIP、RAR、7z)
  • 不同的验证策略(手机号验证、邮箱验证、实名验证)

传统的if-else方式会导致代码难以维护,每次添加新策略都需要修改原有代码。策略模式通过将每种算法封装到独立类中,实现了算法的可互换性和可扩展性。

策略模式的结构

┌─────────────────┐       ┌─────────────────┐
│    Context     │──────▶│   Strategy      │
│   (上下文)      │       │   (抽象策略)    │
└─────────────────┘       └─────────────────┘
                                   │
              ┌──────────────────────┼──────────────────────┐
              │                      │                      │
     ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
     │ ConcreteStrA │ │ ConcreteStrB │ │ ConcreteStrC │
     │ (具体策略A)  │ │ (具体策略B)  │ │ (具体策略C)  │
     └──────────────┘ └──────────────┘ └──────────────┘
Enter fullscreen mode Exit fullscreen mode

核心角色

  1. Strategy(抽象策略):定义所有算法的公共接口
  2. ConcreteStrategy(具体策略):实现具体的算法逻辑
  3. Context(上下文):持有策略引用,根据需要切换算法

代码实现

TypeScript 实现

// 抽象策略接口
interface SortStrategy {
  sort(data: number[]): number[];
}

// 具体策略:快速排序
class QuickSortStrategy implements SortStrategy {
  sort(data: number[]): number[] {
    if (data.length <= 1) return data;

    const pivot = data[0];
    const left = data.slice(1).filter(x => x < pivot);
    const right = data.slice(1).filter(x => x >= pivot);

    return [...this.sort(left), pivot, ...this.sort(right)];
  }
}

// 具体策略:归并排序
class MergeSortStrategy implements SortStrategy {
  sort(data: number[]): number[] {
    if (data.length <= 1) return data;

    const mid = Math.floor(data.length / 2);
    const left = this.sort(data.slice(0, mid));
    const right = this.sort(data.slice(mid));

    return this.merge(left, right);
  }

  private merge(left: number[], right: number[]): number[] {
    const result: number[] = [];
    let i = 0, j = 0;

    while (i < left.length && j < right.length) {
      if (left[i] <= right[j]) {
        result.push(left[i++]);
      } else {
        result.push(right[j++]);
      }
    }

    return [...result, ...left.slice(i), ...right.slice(j)];
  }
}

// 具体策略:冒泡排序(简化版)
class BubbleSortStrategy implements SortStrategy {
  sort(data: number[]): number[] {
    const result = [...data];
    for (let i = 0; i < result.length; i++) {
      for (let j = 0; j < result.length - i - 1; j++) {
        if (result[j] > result[j + 1]) {
          [result[j], result[j + 1]] = [result[j + 1], result[j]];
        }
      }
    }
    return result;
  }
}

// 上下文
class Sorter {
  private strategy: SortStrategy;

  constructor(strategy: SortStrategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy: SortStrategy): void {
    this.strategy = strategy;
  }

  sort(data: number[]): number[] {
    return this.strategy.sort(data);
  }
}

// 使用示例
const data = [64, 34, 25, 12, 22, 11, 90];

const sorter = new Sorter(new QuickSortStrategy());
console.log(sorter.sort(data)); // [11, 12, 22, 25, 34, 64, 90]

// 运行时切换策略
sorter.setStrategy(new MergeSortStrategy());
console.log(sorter.sort(data)); // [11, 12, 22, 25, 34, 64, 90]
Enter fullscreen mode Exit fullscreen mode

Python 实现

from abc import ABC, abstractmethod
from typing import List

# 抽象策略
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float) -> bool:
        pass

# 具体策略:支付宝
class AlipayStrategy(PaymentStrategy):
    def pay(self, amount: float) -> bool:
        print(f"使用支付宝支付 ¥{amount}")
        return True

# 具体策略:微信支付
class WechatPayStrategy(PaymentStrategy):
    def pay(self, amount: float) -> bool:
        print(f"使用微信支付 ¥{amount}")
        return True

# 具体策略:银行卡
class CreditCardStrategy(PaymentStrategy):
    def __init__(self, card_no: str):
        self.card_no = card_no

    def pay(self, amount: float) -> bool:
        print(f"使用信用卡 {self.card_no[-4:]} 支付 ¥{amount}")
        return True

# 上下文
class PaymentContext:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def set_strategy(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def pay(self, amount: float):
        return self.strategy.pay(amount)

# 使用
context = PaymentContext(AlipayStrategy())
context.pay(100.0)

context.set_strategy(CreditCardStrategy("6222 **** **** 1234"))
context.pay(200.0)
Enter fullscreen mode Exit fullscreen mode

实际应用场景

1. 支付系统

支付系统是策略模式最典型的应用场景之一:

interface Payment {
  pay(amount: number): Promise<void>;
}

class Alipay implements Payment { /* ... */ }
class WechatPay implements Payment { /* ... */ }
class CreditCard implements Payment { /* ... */ }
class PayPal implements Payment { /* ... */ }

class PaymentService {
  constructor(private payment: Payment) {}

  async process(amount: number) {
    await this.payment.pay(amount);
  }
}
Enter fullscreen mode Exit fullscreen mode

2. 数据验证

各种验证场景:

interface Validator {
  validate(value: any): boolean;
  errorMessage(): string;
}

class EmailValidator implements Validator { /* ... */ }
class PhoneValidator implements Validator { /* ... */ }
class PasswordValidator implements Validator { /* ... */ }
class IDCardValidator implements Validator { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

3. 日志记录

日志系统:

interface Logger {
  log(level: string, message: string): void;
}

class ConsoleLogger implements Logger { /* ... */ }
class FileLogger implements Logger { /* ... */ }
class CloudLogger implements Logger { /* ... */ }
class CompositeLogger implements Logger { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

策略模式的优缺点

优点

开闭原则:新增策略无需修改现有代码
算法复用:避免重复实现相同逻辑
运行时切换:可以在运行时动态切换算法
单一职责:每个策略类只负责自己的算法
条件分支优化:消除大量的if-else代码

缺点

类数量增加:每个算法都需要一个类
客户端须知:客户端需要了解所有策略的差异
对象创建:需要创建多个策略对象

策略模式 vs 其他模式

模式 目的 关系
策略模式 封装算法 可以与工厂模式结合
状态模式 封装状态变化 策略模式的兄弟,但内部状态会驱动变化
模板方法 封装算法骨架 使用继承,策略使用组合
装饰器模式 动态添加职责 可以与策略模式结合使用

最佳实践

1. 与工厂模式结合

class StrategyFactory {
  private static strategies = new Map<string, () => Strategy>();

  static register(name: string, factory: () => Strategy): void {
    this.strategies.set(name, factory);
  }

  static create(name: string): Strategy {
    const factory = this.strategies.get(name);
    if (!factory) throw new Error(`Strategy ${name} not found`);
    return factory();
  }
}
Enter fullscreen mode Exit fullscreen mode

2. 使用依赖注入

通过DI容器注入策略,实现松耦合。

3. 默认策略

在上下文中设置合理的默认策略。

4. 策略注册表

实现策略的动态注册和发现。

class StrategyRegistry<T> {
  private strategies = new Map<string, T>();

  register(name: string, strategy: T): void {
    this.strategies.set(name, strategy);
  }

  get(name: string): T | undefined {
    return this.strategies.get(name);
  }

  list(): string[] {
    return Array.from(this.strategies.keys());
  }
}
Enter fullscreen mode Exit fullscreen mode

总结

策略模式是一种强大且灵活的设计模式,它的核心思想是:将算法封装到独立的类中,使它们可以互相替换

通过策略模式,你可以:

  • 在运行时动态选择算法
  • 轻松添加新的算法而无需修改现有代码
  • 将算法的实现与使用解耦
  • 提高代码的可维护性和可测试性

在实际开发中,策略模式广泛应用于支付系统、验证系统、日志系统、排序算法等各种场景。掌握这一模式,将大大提升你的代码设计能力。


推荐阅读:结合《深入理解计算机系统》一起学习算法复杂度,能帮助你更好地选择何时使用何种策略。

如果你喜欢这篇文章,请点赞、收藏和关注,我会持续分享更多高质量的技术内容!

设计模式 #软件开发 #后端开发 #编程

Top comments (0)