概念
装饰器模式是一种结构型设计模式,它允许你在不修改原有类的情况下,通过"包裹"的方式动态地为对象添加新功能。与继承不同,它在运行时灵活组合,符合开闭原则(对扩展开放,对修改关闭)。
适用场景
适用于 在基本功能基础上,进行功能叠加场景。
角色
协议:行为抽象
功能组件:遵守协议,实现基础功能
装饰器基类:遵守协议,持有功能组件,调用转发
装饰器扩展:继承自装饰器基类,通过重载,进行行为扩展(功能叠加);每个装饰器只做一件事
示例:咖啡配料系统
定义协议
protocol Coffee {
func description() -> String
func cost() -> Double
}
功能组件,遵守协议。
实现基本功能。
class PlainCoffee: Coffee {
func description() -> String { "简单咖啡" }
func cost() -> Double { 10.0 }
}
基础装饰器
1、持有引用功能组件,同时也遵守协议,转发调用
2、作为基类
class CoffeeDecorator: Coffee {
private let decoratedCoffee: Coffee
init(_ coffee: Coffee) {
self.decoratedCoffee = coffee
}
// 协议调用 转发
func description() -> String {
decoratedCoffee.description()
}
func cost() -> Double {
decoratedCoffee.cost()
}
}
具体装饰器(扩展行为)
继承自 基础装饰器,重载 协议方法;
- 职责单一: 每个装饰器只做一件事,(每种功能分开写在一个子类中
class MilkDecorator: CoffeeDecorator {
override func description() -> String {
super.description() + ",加牛奶"
}
override func cost() -> Double {
super.cost() + 5.0
}
}
class SugarDecorator: CoffeeDecorator {
override func description() -> String {
super.description() + ",加糖"
}
override func cost() -> Double {
super.cost() + 3.0
}
}
class VanillaDecorator: CoffeeDecorator {
override func description() -> String {
super.description() + ",香草风味"
}
override func cost() -> Double {
super.cost() + 8.0
}
}
- 灵活组合
通过构造器注入,功能一层一层加
let plain = PlainCoffee()
print(plain.description()) // 简单咖啡
print(plain.cost()) // 10.0
// 组合 叠加
let milkCoffee = MilkDecorator(plain)
let sugerCoffee = SugarDecorator(milkCoffee)
let fancyCoffee = VanillaDecorator(sugerCoffee)
print(fancyCoffee.description())
// 简单咖啡,加牛奶,加糖,香草风味
print(fancyCoffee.cost())
// 26.0(10 + 5 + 3 + 8)
- 可扩展性:
需要加其他功能?只需再写一个对应的装饰器,插入链中,其他代码零修改。
对比
应用示例——网络请求层
真实 App 的网络层需要叠加多种能力:鉴权、日志、重试、缓存……如果全写在一个类里,既臃肿又难测试。装饰器模式让每个能力独立成一层,按需叠加。
1、协议 — 统一接口
所有网络客户端(真实 + 装饰器)共同遵循的协议
import Foundation
protocol HTTPClient {
func send(
_ request: URLRequest,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
)
}
2、真实组件 — URLSession 封装
final class URLSessionHTTPClient: HTTPClient {
private let session: URLSession
init(session: URLSession = .shared) {
self.session = session
}
func send(
_ request: URLRequest,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
) {
session.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data,
let response = response as? HTTPURLResponse else {
completion(.failure(URLError(.badServerResponse)))
return
}
completion(.success((data, response)))
}.resume()
}
}
3、装饰器1:日志(LogDecorator)
final class LogDecorator: HTTPClient {
private let wrapped: HTTPClient
init(_ client: HTTPClient) {
self.wrapped = client
}
func send(
_ request: URLRequest,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
) {
let start = Date()
print("▶ [\(request.httpMethod ?? "?")] \(request.url?.absoluteString ?? "")")
wrapped.send(request) { result in
let elapsed = String(format: "%.2fms", Date().timeIntervalSince(start) * 1000)
switch result {
case .success(let (data, response)):
print("✅ \(response.statusCode) | \(data.count)B | \(elapsed)")
case .failure(let error):
print("❌ 失败: \(error.localizedDescription) | \(elapsed)")
}
completion(result)
}
}
}
4、装饰器2:自动鉴权(AuthDecorator)
final class AuthDecorator: HTTPClient {
private let wrapped: HTTPClient
private let tokenProvider: () -> String?
init(_ client: HTTPClient, tokenProvider: @escaping () -> String?) {
self.wrapped = client
self.tokenProvider = tokenProvider
}
func send(
_ request: URLRequest,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
) {
var authenticatedRequest = request
if let token = tokenProvider() {
authenticatedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
wrapped.send(authenticatedRequest, completion: completion)
}
}
5、装饰器三:失败重试(RetryDecorator)
final class RetryDecorator: HTTPClient {
private let wrapped: HTTPClient
private let maxRetries: Int
init(_ client: HTTPClient, maxRetries: Int = 3) {
self.wrapped = client
self.maxRetries = maxRetries
}
func send(
_ request: URLRequest,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
) {
attempt(request, retriesLeft: maxRetries, completion: completion)
}
private func attempt(
_ request: URLRequest,
retriesLeft: Int,
completion: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void
) {
wrapped.send(request) { [weak self] result in
guard let self else { return }
switch result {
case .success:
completion(result)
case .failure(let error):
guard retriesLeft > 0 else {
completion(.failure(error))
return
}
print("🔁 重试,剩余次数: \(retriesLeft - 1)")
// 延迟 1s 后重试
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
self.attempt(request, retriesLeft: retriesLeft - 1, completion: completion)
}
}
}
}
}
6、组装
// AppDelegate 或 DI 容器中
func makeHTTPClient() -> HTTPClient {
// 最内层:真实请求
let base = URLSessionHTTPClient()
// 第一层:日志
let logged = LogDecorator(base)
// 第二层:鉴权
let authed = AuthDecorator(logged) {
KeychainManager.shared.accessToken
}
// 第三层:重试
let retrying = RetryDecorator(authed, maxRetries: 3)
return retrying // 业务层只看到 HTTPClient
}
7、调用
final class UserViewModel {
private let client: HTTPClient // 只依赖协议
init(client: HTTPClient) {
self.client = client
}
func fetchProfile(userId: String) {
var request = URLRequest(url: URL(string: "https://api.example.com/users/\(userId)")!)
request.httpMethod = "GET"
client.send(request) { result in
switch result {
case .success(let (data, _)):
// 解析 data...
break
case .failure(let error):
// 处理错误...
break
}
}
}
}
8、扩展
需要加缓存层?只需再写一个 CacheDecorator,插入链中
let cached = CacheDecorator(authed, cache: URLCache.shared)
let retrying = RetryDecorator(cached)