Swift装饰器模式:通过扩展增强功能
装饰器模式在Swift中的应用痛点与解决方案
你是否在开发中遇到以下问题:需要为现有类添加日志记录、性能监控或缓存功能,但又不想修改原始类代码?需要在不同场景下为同一对象动态组合多种功能?Swift装饰器模式(Decorator Pattern)结合属性包装器(Property Wrapper)和函数式编程思想,为这些问题提供了优雅解决方案。本文将系统讲解装饰器模式的实现方式、应用场景及最佳实践,帮助你编写更具扩展性和复用性的Swift代码。
读完本文你将掌握:
- 装饰器模式的核心原理与Swift实现方式
- 利用
@propertyWrapper创建类型安全的装饰器 - 函数装饰器的高阶函数实现技巧
- 装饰器在日志、缓存、验证等场景的实战应用
- 装饰器模式与继承、代理模式的选型对比
装饰器模式核心概念与UML类图
装饰器模式属于结构型设计模式,允许向对象动态添加行为,而无需修改其原始类定义。这种模式通过创建包装对象(装饰器)来包裹真实对象,从而在保持接口一致性的前提下扩展功能。
装饰器模式UML类图
Swift装饰器模式的三种实现方式
| 实现方式 | 特点 | 适用场景 |
|---|---|---|
| 属性包装器(@propertyWrapper) | 编译时检查,语法简洁,适用于属性增强 | 属性验证、存储管理、日志记录 |
| 高阶函数装饰器 | 函数式风格,动态组合,适用于方法增强 | 性能监控、缓存、错误处理 |
| 类装饰器(继承+组合) | 面向对象经典实现,支持多态 | 复杂对象功能扩展 |
Swift属性包装器:类型安全的装饰器实现
Swift 5.1引入的属性包装器(Property Wrapper)是实现装饰器模式的强大工具。它通过@propertyWrapper属性将属性的存储逻辑与业务逻辑分离,提供了一种声明式的装饰器实现方式。
基础属性包装器实现
@propertyWrapper
struct LoggedProperty<Value> {
private var value: Value
private let label: String
init(wrappedValue: Value, label: String) {
self.value = wrappedValue
self.label = label
print("初始化属性 \(label),初始值: \(wrappedValue)")
}
var wrappedValue: Value {
get {
print("读取属性 \(label): \(value)")
return value
}
set {
print("更新属性 \(label): \(value) -> \(newValue)")
value = newValue
}
}
// 投影值,提供额外功能
var projectedValue: Self { self }
// 装饰器额外功能
func reset() {
print("重置属性 \(label)")
value = Value.self is ExpressibleByNilLiteral.Type ? nil as! Value : value
}
}
使用属性包装器装饰类属性
class UserProfile {
@LoggedProperty(label: "用户名")
var username: String = "Guest"
@LoggedProperty(label: "年龄")
var age: Int = 18
}
// 使用示例
let profile = UserProfile()
profile.username = "SwiftDeveloper" // 输出: 更新属性 用户名: Guest -> SwiftDeveloper
print(profile.username) // 输出: 读取属性 用户名: SwiftDeveloper
// 输出: SwiftDeveloper
profile.$username.reset() // 输出: 重置属性 用户名
带参数的属性包装器
通过自定义初始化方法,属性包装器可以支持参数配置,实现更灵活的装饰逻辑:
@propertyWrapper
struct ValidatedProperty<Value: Equatable> {
private var value: Value
private let validationRules: [(Value) -> Bool]
private let errorMessage: String
init(wrappedValue: Value,
validationRules: [(Value) -> Bool],
errorMessage: String) {
self.value = wrappedValue
self.validationRules = validationRules
self.errorMessage = errorMessage
// 验证初始值
guard validate(value) else {
fatalError("初始值验证失败: \(errorMessage)")
}
}
var wrappedValue: Value {
get { value }
set {
guard validate(newValue) else {
fatalError("值验证失败: \(errorMessage)")
}
value = newValue
}
}
private func validate(_ value: Value) -> Bool {
validationRules.allSatisfy { $0(value) }
}
}
// 使用带参数的属性包装器
struct User {
@ValidatedProperty(
validationRules: { $0.count >= 6 },
errorMessage: "密码长度必须至少6个字符"
)
var password: String = "secure123"
@ValidatedProperty(
validationRules: { $0 >= 0 && $0 <= 150 },
errorMessage: "年龄必须在0-150之间"
)
var age: Int = 30
}
函数装饰器:高阶函数实现动态功能增强
函数装饰器通过高阶函数(Higher-Order Function)实现,将原始函数作为参数,返回增强后的新函数。这种方式特别适合为现有函数添加横切关注点(Cross-Cutting Concerns)功能,如日志记录、性能监控、缓存等。
基础函数装饰器实现
// 日志装饰器
func log<Input, Output>(
_ function: @escaping (Input) -> Output,
name: String = #function
) -> (Input) -> Output {
return { input in
print("调用函数 \(name),参数: \(input)")
let start = Date()
let result = function(input)
let duration = Date().timeIntervalSince(start)
print("函数 \(name) 执行完成,耗时: \(duration * 1000)ms,结果: \(result)")
return result
}
}
// 使用装饰器
func calculateSum(_ numbers: [Int]) -> Int {
return numbers.reduce(0, +)
}
// 装饰函数
let loggedSum = log(calculateSum)
// 调用装饰后的函数
let result = loggedSum([1, 2, 3, 4, 5])
// 输出:
// 调用函数 calculateSum(_:),参数: [1, 2, 3, 4, 5]
// 函数 calculateSum(_:) 执行完成,耗时: 0.023ms,结果: 15
装饰器组合与柯里化
通过柯里化(Currying)技术,可以创建带参数的装饰器,并将多个装饰器组合使用:
// 带参数的缓存装饰器
func cached<T: Hashable, U>(
_ function: @escaping (T) -> U,
cache: inout [T: U]
) -> (T) -> U {
return { input in
if let cachedResult = cache[input] {
print("返回缓存结果 for \(input)")
return cachedResult
}
let result = function(input)
cache[input] = result
print("缓存结果 for \(input)")
return result
}
}
// 组合装饰器
func compose<A, B, C>(
_ f: @escaping (B) -> C,
_ g: @escaping (A) -> B
) -> (A) -> C {
return { a in f(g(a)) }
}
// 使用示例
func fetchUserData(userId: Int) -> String {
print("从服务器获取用户数据: \(userId)")
return "User \(userId): \(UUID().uuidString)"
}
// 创建缓存
var userCache: [Int: String] = [:]
// 组合日志和缓存装饰器
let enhancedFetch = compose(
log,
cached(fetchUserData, cache: &userCache)
)
// 第一次调用(缓存未命中)
enhancedFetch(100) // 输出: 从服务器获取用户数据: 100,缓存结果 for 100...
// 第二次调用(缓存命中)
enhancedFetch(100) // 输出: 返回缓存结果 for 100...
方法装饰器实现
通过Swift的@autoclosure和属性包装器结合,可以实现方法装饰器:
class MethodDecorator {
static func measure<T>(
_ function: String = #function,
_ closure: @autoclosure () throws -> T
) rethrows -> T {
let start = CACurrentMediaTime()
defer {
let duration = CACurrentMediaTime() - start
print("方法 \(function) 执行耗时: \(duration * 1000)ms")
}
return try closure()
}
}
// 在类中使用
class DataProcessor {
func processLargeData() {
MethodDecorator.measure {
// 模拟耗时操作
Thread.sleep(forTimeInterval: 0.5)
print("数据处理完成")
}
}
}
// 输出:
// 数据处理完成
// 方法 processLargeData() 执行耗时: 502.3ms
实战应用:装饰器模式解决真实业务问题
1. 网络请求装饰器链
实现一个包含日志记录、错误处理、缓存和重试功能的网络请求装饰器链:
// 定义网络请求类型
typealias NetworkRequest = (URLRequest) async throws -> Data
// 日志装饰器
func withLogging(_ request: @escaping NetworkRequest) -> NetworkRequest {
return { urlRequest in
print("请求: \(urlRequest.url?.absoluteString ?? "未知URL")")
let start = Date()
do {
let data = try await request(urlRequest)
let duration = Date().timeIntervalSince(start)
print("请求成功,耗时: \(duration)s,数据大小: \(data.count) bytes")
return data
} catch {
print("请求失败: \(error.localizedDescription)")
throw error
}
}
}
// 缓存装饰器
func withCaching(_ request: @escaping NetworkRequest, cache: NSCache<NSURL, NSData>) -> NetworkRequest {
return { urlRequest in
guard let url = urlRequest.url as NSURL? else {
return try await request(urlRequest)
}
// 检查缓存
if let cachedData = cache.object(forKey: url) as Data? {
print("使用缓存数据 for \(url)")
return cachedData
}
// 执行请求并缓存结果
let data = try await request(urlRequest)
cache.setObject(data as NSData, forKey: url)
return data
}
}
// 重试装饰器
func withRetry(_ request: @escaping NetworkRequest, maxRetries: Int = 3) -> NetworkRequest {
return { urlRequest in
var retries = 0
while true {
do {
return try await request(urlRequest)
} catch {
retries += 1
if retries >= maxRetries {
throw error
}
let delay = Double(retries) * 0.5 // 指数退避策略
print("请求失败,重试 \(retries)/\(maxRetries),延迟 \(delay)s")
try await Task.sleep(nanoseconds: UInt64(delay * 1e9))
}
}
}
}
// 创建缓存
let cache = NSCache<NSURL, NSData>()
cache.countLimit = 100 // 限制缓存数量
// 构建基础网络请求
let baseRequest: NetworkRequest = { urlRequest in
let (data, _) = try await URLSession.shared.data(for: urlRequest)
return data
}
// 组合装饰器
let enhancedRequest = withLogging(withCaching(withRetry(baseRequest), cache: cache))
// 使用装饰后的请求
let url = URL(string: "https://api.example.com/data")!
let urlRequest = URLRequest(url: url)
do {
let data = try await enhancedRequest(urlRequest)
// 处理数据
} catch {
// 处理错误
}
2. 权限检查装饰器
实现一个基于角色的权限检查装饰器,确保敏感操作只能被授权用户执行:
// 用户角色定义
enum UserRole {
case guest
case user
case admin
}
// 当前用户上下文
class AuthContext {
static var currentRole: UserRole = .guest
}
// 权限检查装饰器
func requireRole(_ requiredRole: UserRole, _ function: @escaping () throws -> Void) rethrows -> Void {
guard AuthContext.currentRole >= requiredRole else {
throw NSError(
domain: "AuthError",
code: 403,
userInfo: [NSLocalizedDescriptionKey: "权限不足,需要\(requiredRole)角色"]
)
}
try function()
}
// 扩展使UserRole可比较
extension UserRole: Comparable {
static func < (lhs: UserRole, rhs: UserRole) -> Bool {
switch (lhs, rhs) {
case (.guest, _): return true
case (.user, .admin): return true
default: return false
}
}
}
// 使用权限装饰器
class AdminPanel {
func deleteUser(userId: Int) throws {
try requireRole(.admin) {
print("删除用户 \(userId)")
// 执行删除操作
}
}
func viewUser(userId: Int) throws {
try requireRole(.user) {
print("查看用户 \(userId) 信息")
// 执行查看操作
}
}
}
// 测试权限控制
let panel = AdminPanel()
AuthContext.currentRole = .user
try panel.viewUser(userId: 100) // 成功执行
try panel.deleteUser(userId: 100) // 抛出权限不足错误
装饰器模式与其他模式的对比分析
装饰器 vs 继承
| 特性 | 装饰器模式 | 继承 |
|---|---|---|
| 功能扩展方式 | 组合 | 类层次结构 |
| 灵活性 | 运行时动态组合 | 编译时静态定义 |
| 代码复用 | 高,装饰器可复用 | 低,易产生类爆炸 |
| 职责单一 | 好,每个装饰器专注单一功能 | 差,父类承担多个职责 |
| 实现复杂度 | 中等,需维护装饰链 | 简单,直接扩展 |
装饰器 vs 代理模式
| 特性 | 装饰器模式 | 代理模式 |
|---|---|---|
| 目的 | 添加功能 | 控制访问 |
| 关系透明度 | 通常对客户端透明 | 通常对客户端不透明 |
| 功能增强 | 主要目的是增强功能 | 主要目的是控制访问 |
| 组合方式 | 可嵌套多个装饰器 | 通常一对一代理 |
装饰器模式适用场景决策树
装饰器模式最佳实践与避坑指南
最佳实践
- 保持单一职责:每个装饰器只负责添加一种功能,便于复用和维护
- 支持装饰器组合:设计时考虑多个装饰器的嵌套使用
- 透明接口:装饰器应实现与被装饰对象相同的接口,对客户端透明
- 提供撤销机制:复杂装饰链可考虑实现功能撤销能力
- 使用协议定义组件接口:确保装饰器和被装饰对象遵循同一协议
常见陷阱与解决方案
| 陷阱 | 解决方案 |
|---|---|
| 装饰器链过长导致调试困难 | 使用明确的命名和日志记录,限制单个对象的装饰器数量 |
| 过度使用导致系统复杂度增加 | 对于简单功能使用扩展(Extension)而非装饰器 |
| 装饰器顺序依赖问题 | 明确定义装饰器顺序规则,提供组合函数管理顺序 |
| 性能开销 | 避免在高频调用路径使用多层装饰器,考虑编译时优化 |
性能考量
装饰器模式会引入一定的性能开销,主要来自:
- 额外的函数调用层级
- 装饰器实例创建
- 装饰链遍历
优化建议:
- 对于性能关键路径,考虑编译时优化或代码生成
- 使用
@inline(__always)内联小型装饰器函数 - 对装饰器链进行缓存,避免重复创建
- 优先使用属性包装器而非运行时装饰器链
总结与进阶方向
装饰器模式为Swift开发提供了灵活的功能扩展机制,通过属性包装器、高阶函数等实现方式,可以在不修改原始代码的情况下为对象动态添加功能。本文介绍了装饰器模式的核心概念、三种实现方式及实战应用,对比了装饰器与继承、代理模式的差异,并提供了最佳实践指南。
进阶学习方向
- 元编程与代码生成:结合Swift macros创建更强大的编译时装饰器
- 函数式响应式编程:结合Combine框架实现响应式装饰器
- AOP编程:探索Aspect-Oriented Programming在Swift中的实现
- 依赖注入:结合装饰器模式实现更灵活的依赖管理
- SwiftUI中的装饰器:探索ViewModifier作为UI组件的装饰器实现
装饰器模式在Swift生态中的应用
- SwiftUI:
ViewModifier本质上是装饰器模式的实现 - Combine:操作符(Operator)链是函数装饰器的响应式实现
- SwiftData:属性包装器如
@Query、@Attribute实现数据访问装饰 - Networking库:如Alamofire的RequestAdapter和RequestRetrier
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



