策略模式深度指南:封装可替换算法的艺术
策略模式(Strategy Pattern)是一种行为设计模式,它让你能在运行时选择算法的行为。就像在餐厅点餐时,可以从菜单中选择不同的烹饪方式——可以清蒸、红烧或油炸,每种方式都封装了一种具体的算法。
为什么要使用策略模式?
在软件开发中,我们经常遇到需要根据不同情况使用不同算法的场景:
- 不同的支付方式(支付宝、微信、银行卡)
- 不同的排序算法(快速排序、归并排序、堆排序)
- 不同的压缩算法(ZIP、RAR、7z)
- 不同的验证策略(手机号验证、邮箱验证、实名验证)
传统的if-else方式会导致代码难以维护,每次添加新策略都需要修改原有代码。策略模式通过将每种算法封装到独立类中,实现了算法的可互换性和可扩展性。
策略模式的结构
┌─────────────────┐ ┌─────────────────┐
│ Context │──────▶│ Strategy │
│ (上下文) │ │ (抽象策略) │
└─────────────────┘ └─────────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ConcreteStrA │ │ ConcreteStrB │ │ ConcreteStrC │
│ (具体策略A) │ │ (具体策略B) │ │ (具体策略C) │
└──────────────┘ └──────────────┘ └──────────────┘
核心角色
- Strategy(抽象策略):定义所有算法的公共接口
- ConcreteStrategy(具体策略):实现具体的算法逻辑
- 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]
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)
实际应用场景
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);
}
}
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 { /* ... */ }
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 { /* ... */ }
策略模式的优缺点
优点
✅ 开闭原则:新增策略无需修改现有代码
✅ 算法复用:避免重复实现相同逻辑
✅ 运行时切换:可以在运行时动态切换算法
✅ 单一职责:每个策略类只负责自己的算法
✅ 条件分支优化:消除大量的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();
}
}
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());
}
}
总结
策略模式是一种强大且灵活的设计模式,它的核心思想是:将算法封装到独立的类中,使它们可以互相替换。
通过策略模式,你可以:
- 在运行时动态选择算法
- 轻松添加新的算法而无需修改现有代码
- 将算法的实现与使用解耦
- 提高代码的可维护性和可测试性
在实际开发中,策略模式广泛应用于支付系统、验证系统、日志系统、排序算法等各种场景。掌握这一模式,将大大提升你的代码设计能力。
推荐阅读:结合《深入理解计算机系统》一起学习算法复杂度,能帮助你更好地选择何时使用何种策略。
如果你喜欢这篇文章,请点赞、收藏和关注,我会持续分享更多高质量的技术内容!
Top comments (0)