Swift装饰器模式:通过扩展增强功能

Swift装饰器模式:通过扩展增强功能

装饰器模式在Swift中的应用痛点与解决方案

你是否在开发中遇到以下问题:需要为现有类添加日志记录、性能监控或缓存功能,但又不想修改原始类代码?需要在不同场景下为同一对象动态组合多种功能?Swift装饰器模式(Decorator Pattern)结合属性包装器(Property Wrapper)和函数式编程思想,为这些问题提供了优雅解决方案。本文将系统讲解装饰器模式的实现方式、应用场景及最佳实践,帮助你编写更具扩展性和复用性的Swift代码。

读完本文你将掌握:

  • 装饰器模式的核心原理与Swift实现方式
  • 利用@propertyWrapper创建类型安全的装饰器
  • 函数装饰器的高阶函数实现技巧
  • 装饰器在日志、缓存、验证等场景的实战应用
  • 装饰器模式与继承、代理模式的选型对比

装饰器模式核心概念与UML类图

装饰器模式属于结构型设计模式,允许向对象动态添加行为,而无需修改其原始类定义。这种模式通过创建包装对象(装饰器)来包裹真实对象,从而在保持接口一致性的前提下扩展功能。

装饰器模式UML类图

mermaid

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 代理模式

特性装饰器模式代理模式
目的添加功能控制访问
关系透明度通常对客户端透明通常对客户端不透明
功能增强主要目的是增强功能主要目的是控制访问
组合方式可嵌套多个装饰器通常一对一代理

装饰器模式适用场景决策树

mermaid

装饰器模式最佳实践与避坑指南

最佳实践

  1. 保持单一职责:每个装饰器只负责添加一种功能,便于复用和维护
  2. 支持装饰器组合:设计时考虑多个装饰器的嵌套使用
  3. 透明接口:装饰器应实现与被装饰对象相同的接口,对客户端透明
  4. 提供撤销机制:复杂装饰链可考虑实现功能撤销能力
  5. 使用协议定义组件接口:确保装饰器和被装饰对象遵循同一协议

常见陷阱与解决方案

陷阱解决方案
装饰器链过长导致调试困难使用明确的命名和日志记录,限制单个对象的装饰器数量
过度使用导致系统复杂度增加对于简单功能使用扩展(Extension)而非装饰器
装饰器顺序依赖问题明确定义装饰器顺序规则,提供组合函数管理顺序
性能开销避免在高频调用路径使用多层装饰器,考虑编译时优化

性能考量

装饰器模式会引入一定的性能开销,主要来自:

  • 额外的函数调用层级
  • 装饰器实例创建
  • 装饰链遍历

优化建议:

  • 对于性能关键路径,考虑编译时优化或代码生成
  • 使用@inline(__always)内联小型装饰器函数
  • 对装饰器链进行缓存,避免重复创建
  • 优先使用属性包装器而非运行时装饰器链

总结与进阶方向

装饰器模式为Swift开发提供了灵活的功能扩展机制,通过属性包装器、高阶函数等实现方式,可以在不修改原始代码的情况下为对象动态添加功能。本文介绍了装饰器模式的核心概念、三种实现方式及实战应用,对比了装饰器与继承、代理模式的差异,并提供了最佳实践指南。

进阶学习方向

  1. 元编程与代码生成:结合Swift macros创建更强大的编译时装饰器
  2. 函数式响应式编程:结合Combine框架实现响应式装饰器
  3. AOP编程:探索Aspect-Oriented Programming在Swift中的实现
  4. 依赖注入:结合装饰器模式实现更灵活的依赖管理
  5. SwiftUI中的装饰器:探索ViewModifier作为UI组件的装饰器实现

装饰器模式在Swift生态中的应用

  • SwiftUIViewModifier本质上是装饰器模式的实现
  • Combine:操作符(Operator)链是函数装饰器的响应式实现
  • SwiftData:属性包装器如@Query@Attribute实现数据访问装饰
  • Networking库:如Alamofire的RequestAdapter和RequestRetrier

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值