06_策略模式 Strategy Pattern
1. 基本信息
中文名称: 策略模式
英文名称: Strategy Pattern
模式类型: 行为型设计模式
Rust 中常见实现方式: trait、泛型、dyn Trait、闭包
策略模式的核心是:将可变化的算法或行为封装起来,使调用方依赖抽象而不是具体实现,从而可以在运行时或编译期灵活替换算法或策略。
2. 模式核心思想
策略模式的核心思想是:
将算法或行为从调用方分离出来,通过统一接口封装不同实现,调用方无需修改即可选择不同策略。
换句话说,策略模式强调“行为可替换性”和“解耦算法与使用方”。
3. 这个模式解决什么问题
在实际开发中,策略模式主要解决以下问题:
一个功能存在多种实现方式,调用方需要灵活选择。
避免在调用方写大量 if/else 或 match 判断。
算法可能经常变化,需要便于扩展。
保持调用方代码稳定,减少对具体算法的依赖。
例如:
- 不同排序算法(冒泡、快速、归并)可替换
- 支付方式(微信、支付宝、银行卡)可动态选择
- 文件压缩方式(zip、gzip、none)可灵活切换
- 验证策略(用户名、邮箱、手机号)可以自由组合
策略模式让调用方只依赖抽象接口,而不关心具体实现。
4. 不使用这个模式会怎样
如果不使用策略模式,可能出现:
调用方代码中大量 if/else 分支判断算法类型。
算法逻辑和调用方耦合,修改算法影响调用方。
代码扩展性差,每增加一种算法都需要修改调用方。
重复代码多,维护难度增加。
5. 传统面向对象中的实现思路
在传统 OOP 语言中,策略模式通常有三个角色:
Strategy 接口:定义算法抽象。
ConcreteStrategy:具体算法实现。
Context:持有策略对象,调用策略方法。
调用方通过设置不同策略对象来改变行为,而不修改 Context 内部逻辑。
6. Rust 中的实现思路
6.1 Rust 中通常怎么实现
Rust 中策略模式通常依赖 trait 作为策略接口:
trait Strategy {
fn execute(&self, data: &mut Vec<i32>);
}
struct BubbleSort;
struct QuickSort;
impl Strategy for BubbleSort {
fn execute(&self, data: &mut Vec<i32>) { data.sort(); }
}
impl Strategy for QuickSort {
fn execute(&self, data: &mut Vec<i32>) { data.sort(); }
}
fn run_strategy<S: Strategy>(strategy: S, data: &mut Vec<i32>) {
strategy.execute(data);
}
调用方可以选择不同的策略:
let mut nums = vec![3, 1, 2];
run_strategy(BubbleSort, &mut nums);
动态分发也可以通过 dyn Strategy:
let strategy: Box<dyn Strategy> = Box::new(QuickSort);
strategy.execute(&mut nums);
6.2 和传统 OOP 写法相比有什么不同
- Rust 不依赖类和继承,策略由 trait + impl 实现。
- 静态分发(泛型)可在编译期确定策略,性能接近内联函数。
- 动态分发(dyn Trait)允许运行时选择策略,但有轻微开销。
- 不需要上下文类强耦合,Context 可以是简单函数或结构体。
6.3 Rust 中是否有更自然的替代写法
- 对简单逻辑,可直接使用闭包替代 trait:
let bubble_sort = |data: &mut Vec<i32>| data.sort();
bubble_sort(&mut nums);
- 对可组合的策略,闭包或迭代器链可以简化实现。
- Rust 强调编译期安全和零成本抽象,静态分发泛型策略是最自然写法。
7. Rust 中涉及的语言特性
trait
泛型
dyn Trait
impl
闭包(Function-like Strategy)
所有权和借用
结构体作为 Context 可选
这些特性分别解决的问题:
- trait:行为抽象
- 泛型:静态分发策略
- dyn Trait:动态策略选择
- 所有权和借用:安全管理数据访问
8. 性能与工程代价
- 静态分发策略几乎零成本,编译器会内联执行。
- 动态分发策略需要一次虚函数调用,开销略高。
- 如果策略链复杂,可能导致闭包嵌套较深,需要注意类型复杂性。
- Rust 的 trait object 和泛型组合让策略模式既安全又高效。
9. 典型应用场景
排序算法选择
支付方式动态选择
文件压缩方式选择
验证或规则策略
数据处理策略(过滤、转换、聚合)
10. 和相似模式的区别
- 状态模式:状态模式关注对象状态变化后的行为不同;策略模式关注算法或行为本身可替换。
- 命令模式:命令模式把操作封装成对象,可以延迟执行;策略模式只提供可替换算法接口。
- 模板方法模式:模板方法固定流程,部分步骤可变;策略模式整个算法可替换。
11. 使用该模式的优点
避免大量 if/else 分支判断
算法可以独立扩展和修改
调用方与具体算法解耦
可组合或动态切换策略
静态分发策略性能高
12. 使用该模式的代价
动态策略需要虚函数调用开销
策略数量过多,可能增加类型复杂性
对简单算法使用可能显得冗余
调试闭包组合策略可能稍复杂
13. 什么时候不应该使用
算法只有一种,没必要抽象
调用方和算法耦合变化不频繁
对性能要求极端,需要避免虚函数
对象行为高度依赖内部状态,可能更适合状态模式
14. 一个简单例子思路
场景: 文件压缩工具
- 支持 zip、gzip、none 三种压缩方式
- 调用方只选择策略,无需关心具体实现
处理流程:
定义 Strategy trait
为每种压缩算法实现 trait
调用方持有 trait 对象(泛型或 Box<dyn Strategy>)
执行压缩操作
可以动态替换策略
适合练习 Rust 特性:trait、泛型、dyn Trait、闭包、所有权与借用。
15. 总结一句话
策略模式的本质是:
将可变算法或行为封装起来,调用方依赖抽象而不是具体实现,从而可以在运行时或编译期灵活切换策略。在 Rust 中,trait、泛型和闭包是实现策略模式的核心工具。