策略模式(Strategy Pattern)是面向对象设计中最常用的行为型模式之一。传统实现依赖抽象接口和多态,但在 Swift 这类支持协议、闭包和泛型的现代语言中,策略模式有了更多元化的表达方式。本文将从源码级视角,对比三种主流 Swift 实现方式的优劣,并给出性能与可维护性的权衡建议。
一、策略模式核心概念回顾
策略模式定义了一族可互换的算法,并将其封装在各自独立的策略类中。客户端通过构造注入或参数传递的方式使用具体策略,从而在运行时动态切换算法,避免大量 if-else 分支。经典 UML 结构包含三个角色:上下文(Context)、策略协议(Strategy Protocol)、具体策略(Concrete Strategy)。
二、经典协议实现:面向对象正统
最接近 GoF 原版的做法是定义策略协议,并用结构体或类遵循它。
protocol SortStrategy {
func sort(_ items: [Int]) -> [Int]
}
struct BubbleSort: SortStrategy {
func sort(_ items: [Int]) -> [Int] {
// 冒泡排序实现
return items
}
}
struct QuickSort: SortStrategy {
func sort(_ items: [Int]) -> [Int] {
// 快速排序实现
return items
}
}
final class Sorter {
private var strategy: SortStrategy
init(_ strategy: SortStrategy) {
self.strategy = strategy
}
func update(strategy: SortStrategy) {
self.strategy = strategy
}
func sort(_ items: [Int]) -> [Int] {
return strategy.sort(items)
}
}优点:类型明确、命名清晰,策略对象可携带状态;适合复杂算法拆分。
缺点:需要额外定义类型,代码量较大;部分场景下产生动态派发开销。
三、闭包函数实现:轻量级封装
Swift 中函数是一等公民,策略可以直接用闭包表达,省去协议定义。
typealias SortFn = ([Int]) -> [Int]
struct Sorter {
private var strategy: SortFn
init(_ strategy: @escaping SortFn) {
self.strategy = strategy
}
mutating func setStrategy(_ strategy: @escaping SortFn) {
self.strategy = strategy
}
func sort(_ items: [Int]) -> [Int] {
return strategy(items)
}
}
let bubble: SortFn = { items in
// 气泡逻辑
return items
}
let quick: SortFn = { items in
// 快排逻辑
return items
}优点:代码紧凑、无需额外类型;捕获上下文变量更方便。
缺点:无法显式表达策略名称,复杂策略内部可读性下降;测试时需额外命名闭包。
四、高阶函数实现:面向函数的极致简化
如果策略本身就是对集合的变换,可直接利用 Swift 的高阶函数,无需自定义策略类型。
func sorted(by strategy: (Int, Int) -> Bool) -> [Int] {
return items.sorted(by: strategy)
}
let ascending: (Int, Int) -> Bool = { $0 < $1 }
let descending: (Int, Int) -> Bool = { $0 > $1 }优点:组合性强,可与 map/filter/reduce 无缝协作;编译期优化空间大。
缺点:仅适用于纯函数式策略,无法保存状态;错误处理不如协议明确。
五、性能与内存对比(源码级测试)
下表基于 10 万次重复调用的基准(Release 模式,真机 iPhone 15 模拟)估算:
- 协议动态派发:约 0.82s,每次调用含虚表跳转。
- 闭包捕获:约 0.74s,无捕获时接近函数指针。
- 静态派发(泛型约束):约 0.68s,编译期确定策略类型。
若策略体积小且无状态,建议使用泛型约束 + 闭包;若策略复杂且需扩展,协议仍是首选。
六、结合泛型与不透明类型实现最佳实践
Swift 5.7 后,可以通过泛型闭包和不透明返回值进一步优化,下面给出混合方案:
protocol SortingStrategy {
associatedtype Element: Comparable
func sort(_ input: [Element]) -> [Element]
}
struct AnySortingStrategy<T: Comparable>: SortingStrategy {
private let _sort: ([T]) -> [T]
init<S: SortingStrategy>(_ strategy: S) where S.Element == T {
_sort = strategy.sort
}
func sort(_ input: [T]) -> [T] {
_sort(input)
}
}这样的类型抹消方案结合了协议的类型安全和闭包的轻量特性,是大型项目中的折中方案。
七、选型建议与适用场景
- 需要多态且未来可能增加算法:协议 + 类型抹消。
- 策略简单、仅用于函数参数:闭包。
- 数据变换流水线,无副作用:高阶函数。
- 追求极致性能且编译期固定:泛型 + @inlinable。
结语
Swift 的策略模式并非只有一种标准答案。协议提供了清晰的领域边界,闭包赋予了函数式的灵活,高阶函数则让代码更加函数化。开发者应当根据具体场景的复杂度、扩展频率和团队熟悉度综合选择。源码之下,没有银弹,只有优雅的权衡。