定义
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态添加新功能,同时又不改变其结构。装饰器通过创建一个包装对象来包裹真实对象,实现在不修改原有对象的基础上扩展功能。
装饰器模式的核心思想是:使用组合而非继承来扩展对象的功能,遵循"对扩展开放,对修改关闭"的原则。
为什么需要装饰器模式
装饰器模式要解决的核心问题是:在不修改原有对象的情况下,动态地给对象添加新功能。
问题场景
假设我们正在开发一个UI组件库,需要为UILabel添加各种视觉效果:圆角、阴影、边框、渐变背景等。而且这些效果需要能够自由组合。
方案一:使用继承(不推荐)
// 基础标签
class BasicLabel: UILabel {
init(text: String) {
super.init(frame: .zero)
self.text = text
}
}
// 圆角标签
class RoundedLabel: BasicLabel {
override init(text: String) {
super.init(text: text)
layer.cornerRadius = 8
clipsToBounds = true
}
}
// 阴影标签
class ShadowLabel: BasicLabel {
override init(text: String) {
super.init(text: text)
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.3
}
}
// 边框标签
class BorderLabel: BasicLabel {
override init(text: String) {
super.init(text: text)
layer.borderWidth = 1
layer.borderColor = UIColor.gray.cgColor
}
}
// 如果需要圆角+阴影呢?
class RoundedShadowLabel: BasicLabel {
override init(text: String) {
super.init(text: text)
// 重复圆角代码
layer.cornerRadius = 8
clipsToBounds = true
// 重复阴影代码
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.3
}
}
// 如果需要圆角+边框呢?
class RoundedBorderLabel: BasicLabel { /* 又要重复代码 */ }
// 如果需要阴影+边框呢?
class ShadowBorderLabel: BasicLabel { /* 继续重复代码 */ }
// 如果需要圆角+阴影+边框呢?
class RoundedShadowBorderLabel: BasicLabel { /* 所有代码都重复一遍 */ }
这种方式的问题:
类爆炸:3个效果就需要7个类(2³-1种组合)
- 单独效果:
RoundedLabel、ShadowLabel、BorderLabel - 两两组合:
RoundedShadowLabel、RoundedBorderLabel、ShadowBorderLabel - 三者组合:
RoundedShadowBorderLabel
- 单独效果:
代码重复:每个组合类都要重复实现相同的效果代码
无法动态组合:继承关系在编译时确定,运行时无法改变
扩展困难:如果再添加一个"渐变背景"效果,需要新增15个类!
方案二:装饰器模式(推荐)
使用组合代替继承,每个效果封装为一个装饰器:
// 1. 定义组件协议
protocol LabelComponent {
var label: UILabel { get }
}
// 2. 基础组件
class BasicLabel: LabelComponent {
let label: UILabel
init(text: String) {
label = UILabel()
label.text = text
label.backgroundColor = .white
label.textColor = .black
label.textAlignment = .center
label.font = .systemFont(ofSize: 16)
}
}
// 3. 装饰器基类
class LabelDecorator: LabelComponent {
let wrapped: LabelComponent
var label: UILabel { wrapped.label }
init(_ component: LabelComponent) {
self.wrapped = component
}
}
// 4. 具体装饰器
class RoundedDecorator: LabelDecorator {
override init(_ component: LabelComponent) {
super.init(component)
label.layer.cornerRadius = 8
label.clipsToBounds = true
print("✅ 添加圆角效果")
}
}
class ShadowDecorator: LabelDecorator {
override init(_ component: LabelComponent) {
super.init(component)
label.layer.shadowColor = UIColor.black.cgColor
label.layer.shadowOffset = CGSize(width: 0, height: 2)
label.layer.shadowRadius = 4
label.layer.shadowOpacity = 0.3
label.layer.masksToBounds = false
print("✅ 添加阴影效果")
}
}
class BorderDecorator: LabelDecorator {
override init(_ component: LabelComponent) {
super.init(component)
label.layer.borderWidth = 2
label.layer.borderColor = UIColor.systemBlue.cgColor
print("✅ 添加边框效果")
}
}
class GradientDecorator: LabelDecorator {
override init(_ component: LabelComponent) {
super.init(component)
let gradient = CAGradientLayer()
gradient.colors = [
UIColor.systemBlue.cgColor,
UIColor.systemPurple.cgColor
]
gradient.frame = label.bounds
label.layer.insertSublayer(gradient, at: 0)
print("✅ 添加渐变背景")
}
}
// 5. 使用:自由组合!
var component: LabelComponent = BasicLabel(text: "Hello Decorator")
// 场景1:只要圆角
component = RoundedDecorator(component)
// 输出:✅ 添加圆角效果
// 场景2:圆角 + 阴影
var component2: LabelComponent = BasicLabel(text: "Hello")
component2 = RoundedDecorator(component2)
component2 = ShadowDecorator(component2)
// 输出:
// ✅ 添加圆角效果
// ✅ 添加阴影效果
// 场景3:圆角 + 边框 + 渐变
var component3: LabelComponent = BasicLabel(text: "Hello")
component3 = RoundedDecorator(component3)
component3 = BorderDecorator(component3)
component3 = GradientDecorator(component3)
// 输出:
// ✅ 添加圆角效果
// ✅ 添加边框效果
// ✅ 添加渐变背景
// 场景4:全部效果
var component4: LabelComponent = BasicLabel(text: "Hello")
component4 = RoundedDecorator(component4)
component4 = ShadowDecorator(component4)
component4 = BorderDecorator(component4)
component4 = GradientDecorator(component4)
// 获取最终的UILabel
let finalLabel = component4.label
view.addSubview(finalLabel)
装饰器模式的核心优势:
- ✅ 灵活组合:效果可以任意叠加,无需预定义所有组合
- ✅ 单一职责:每个装饰器只负责一个效果
- ✅ 运行时扩展:可以在运行时动态添加或移除效果
- ✅ 开闭原则:添加新效果不需要修改现有代码
- ✅ 代码复用:每个效果只实现一次,可以在任何地方复用
模式结构
classDiagram
class Component {
<<interface>>
+operation()
}
class ConcreteComponent {
+operation()
}
class Decorator {
<<abstract>>
-component: Component
+operation()
}
class ConcreteDecoratorA {
+operation()
+addedBehaviorA()
}
class ConcreteDecoratorB {
+operation()
+addedBehaviorB()
}
Component <|.. ConcreteComponent
Component <|.. Decorator
Decorator <|-- ConcreteDecoratorA
Decorator <|-- ConcreteDecoratorB
Decorator o-- Component
角色说明
- Component(组件接口):定义对象接口,可以动态地添加职责
- ConcreteComponent(具体组件):定义一个将要接收附加职责的对象
- Decorator(装饰器基类):持有一个Component对象的引用,定义与Component一致的接口
- ConcreteDecorator(具体装饰器):向组件添加具体的职责
iOS中的装饰器实现方式
1. Category(Objective-C)
Category是Objective-C中为现有类添加方法的方式,可以看作是一种简化的装饰器:
// UIView+RoundCorners.h
@interface UIView (RoundCorners)
- (void)applyRoundCorners:(CGFloat)radius;
- (void)applyShadow:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(CGFloat)opacity;
@end
// UIView+RoundCorners.m
@implementation UIView (RoundCorners)
- (void)applyRoundCorners:(CGFloat)radius {
self.layer.cornerRadius = radius;
self.clipsToBounds = YES;
}
- (void)applyShadow:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(CGFloat)opacity {
self.layer.shadowColor = color.CGColor;
self.layer.shadowOffset = offset;
self.layer.shadowRadius = radius;
self.layer.shadowOpacity = opacity;
self.clipsToBounds = NO;
}
@end
// 使用
UIView *view = [[UIView alloc] init];
[view applyRoundCorners:10.0];
[view applyShadow:[UIColor blackColor] offset:CGSizeMake(0, 2) radius:4.0 opacity:0.1];
2. Extension(Swift)
Swift的Extension类似于Category,但功能更强大:
// 为UIView添加装饰方法
extension UIView {
@discardableResult
func roundCorners(_ radius: CGFloat) -> Self {
layer.cornerRadius = radius
clipsToBounds = true
return self
}
@discardableResult
func border(width: CGFloat, color: UIColor) -> Self {
layer.borderWidth = width
layer.borderColor = color.cgColor
return self
}
@discardableResult
func shadow(color: UIColor, offset: CGSize, radius: CGFloat, opacity: Float) -> Self {
layer.shadowColor = color.cgColor
layer.shadowOffset = offset
layer.shadowRadius = radius
layer.shadowOpacity = opacity
clipsToBounds = false
return self
}
}
// 链式调用
let decoratedView = UIView()
.roundCorners(10)
.border(width: 1, color: .gray)
.shadow(color: .black, offset: CGSize(width: 0, height: 2), radius: 4, opacity: 0.1)
3. 包装类装饰器
// 组件协议
protocol DataSource {
func fetchData() -> [String]
}
// 具体组件
class NetworkDataSource: DataSource {
func fetchData() -> [String] {
print("Fetching data from network...")
return ["Item 1", "Item 2", "Item 3"]
}
}
// 装饰器基类
class DataSourceDecorator: DataSource {
let wrappedDataSource: DataSource
init(dataSource: DataSource) {
self.wrappedDataSource = dataSource
}
func fetchData() -> [String] {
return wrappedDataSource.fetchData()
}
}
// 缓存装饰器
class CachingDecorator: DataSourceDecorator {
private var cache: [String]?
private var cacheTimestamp: Date?
private let cacheDuration: TimeInterval
init(dataSource: DataSource, cacheDuration: TimeInterval = 60) {
self.cacheDuration = cacheDuration
super.init(dataSource: dataSource)
}
override func fetchData() -> [String] {
if let cache = cache,
let timestamp = cacheTimestamp,
Date().timeIntervalSince(timestamp) < cacheDuration {
print("Returning cached data")
return cache
}
let data = super.fetchData()
cache = data
cacheTimestamp = Date()
return data
}
}
// 日志装饰器
class LoggingDecorator: DataSourceDecorator {
override func fetchData() -> [String] {
let startTime = Date()
print("[\(startTime)] Starting data fetch...")
let data = super.fetchData()
let endTime = Date()
let duration = endTime.timeIntervalSince(startTime)
print("[\(endTime)] Data fetch completed in \(duration)s, returned \(data.count) items")
return data
}
}
// 加密装饰器
class EncryptionDecorator: DataSourceDecorator {
override func fetchData() -> [String] {
let data = super.fetchData()
print("Encrypting data...")
return data.map { encrypt($0) }
}
private func encrypt(_ string: String) -> String {
// 简单的加密示例
return String(string.reversed())
}
}
// 使用 - 装饰器可以组合
var dataSource: DataSource = NetworkDataSource()
dataSource = CachingDecorator(dataSource: dataSource)
dataSource = LoggingDecorator(dataSource: dataSource)
let data = dataSource.fetchData()
// 第一次调用:从网络获取并缓存
let cachedData = dataSource.fetchData()
// 第二次调用:返回缓存数据
使用场景
- 动态添加功能:需要在运行时为对象添加额外行为
- 透明扩展:扩展功能时不影响其他对象
- 功能组合:需要通过组合多个功能来创建不同变体
- 替代继承:继承方式不合适或导致类爆炸时
优缺点
优点
- 灵活性:比继承更灵活地扩展功能
- 单一职责:每个装饰器只关注一个功能
- 开闭原则:无需修改现有代码即可添加新装饰器
- 组合能力:可以用多个装饰器组合出复杂功能
缺点
- 复杂性:大量装饰器会增加系统复杂度
- 调试困难:多层装饰器导致调试困难
- 顺序依赖:装饰器顺序可能影响结果
最佳实践
- 保持装饰器简单:每个装饰器只添加一个职责
- 使用协议定义接口:保证装饰器和被装饰对象接口一致
- 注意装饰顺序:某些情况下顺序会影响结果
- 考虑使用Extension:简单场景使用Swift Extension更简洁