Decorator, Design Patterns Series -Part 6
👋 Hey iOS devs! Have you ever wanted to add superpowers to an object without rewriting its entire class? Or maybe you’ve groaned at the…
Decorator, Design Patterns Series -Part 6
Photo by Carolina Nichitin on Unsplash
👋 Hey iOS devs! Have you ever wanted to add superpowers to an object without rewriting its entire class? Or maybe you’ve groaned at the idea of subclassing just to tweak one tiny behavior?
Meet the Decorator pattern — a structural hero that lets you wrap objects in layers of functionality like a code burrito. 🌯
The Problem: Inheritance Avalanche
Let’s say you want to customize a UILabel. What do you do?
1️⃣ Subclass UILabel to add a border.
2️⃣ Subclass again to add a shadow.
3️⃣ Subclass again to add a gradient.
What is the Decorator Pattern?
The Decorator pattern allows you to dynamically add responsibilities to objects without modifying their original code. Think of it like Russian nesting dolls — each layer adds new behavior while preserving the core.
Key Players in the Decorator Pattern:
👨💻 Component Protocol — Defines the core object’s interface (e.g., View in SwiftUI).
📦 Concrete Component – The base object (e.g., Text, UIImageView).
🎨 Decorator – Wraps a component to extend or modify its behavior.
SwiftUI’s Open Secret: Modifiers Are Decorators!
Every time you chain .modifier() calls, you’re using the Decorator pattern in action.
Text("Hello")
.padding() // Wraps Text in a PaddingView
.background(.blue) // Wraps PaddingView in a BackgroundView
.cornerRadius(8) // Wraps BackgroundView in a CornerRadiusView
Each modifier wraps the previous view to add new behavior — no subclassing required! Pure magic, right? ✨
Bringing Decorator to UIKit: Dynamic Label Styling
While SwiftUI has this baked in, UIKit devs can also benefit from decorators. Let’s build a DecoratableLabel that allows us to dynamically apply borders, shadows, and more!
Step 1: Define a Decorator Protocol
protocol LabelDecorator {
func decorate(_ label: UILabel)
}
Step 2: Create a Customizable Label
class DecoratableLabel: UILabel {
private var decorators: [LabelDecorator] = []
func addDecorator(_ decorator: LabelDecorator) -> Self {
decorators.append(decorator)
return self
}
func applyDecorators() {
decorators.forEach { $0.decorate(self) }
}
}
Step 3: Implement Decorators
struct BorderDecorator: LabelDecorator {
let color: UIColor
func decorate(_ label: UILabel) {
label.layer.borderColor = color.cgColor
label.layer.borderWidth = 1
}
}
struct ShadowDecorator: LabelDecorator {
func decorate(_ label: UILabel) {
label.layer.shadowOpacity = 0.2
label.layer.shadowRadius = 3
}
}
Step 4: Apply Decorators Dynamically
let label = DecoratableLabel()
.addDecorator(BorderDecorator(color: .blue))
.addDecorator(ShadowDecorator())
label.text = "Decorated!"
label.applyDecorators()
Boom! 🎉 No subclassing. No messy inheritance trees. Just clean, reusable code.
When to Use Decorator? ✅
✅ Adding Optional Features — Like logging, caching, or analytics. ✅ Avoiding Subclass Explosion — Say goodbye to rigid hierarchies. ✅ Runtime Flexibility — Apply/remove behaviors dynamically.
When Not to Use Decorator? 🚫
❌ For Simple Additions — If it’s just a one-off change, a subclass might be fine. ❌ Performance-Critical Code — Too many wrappers can slow down rendering.
Best Practices in Swift
1️⃣ Leverage Protocol Extensions for Flexibility
protocol DataFetcher {
func fetch() -> Data
}
struct LoggerDecorator: DataFetcher {
private let wrapped: DataFetcher
init(_ wrapped: DataFetcher) { self.wrapped = wrapped }
func fetch() -> Data {
print("Fetching data...")
return wrapped.fetch()
}
}
let fetcher = LoggerDecorator(NetworkFetcher())
2️⃣ Create Custom SwiftUI Modifiers
Instead of repeating .shadow() everywhere, create reusable view modifiers:
struct NeonStyle: ViewModifier {
func body(content: Content) -> some View {
content
.shadow(color: .blue, radius: 10)
.foregroundStyle(.cyan)
}
}
Text("GLOW").modifier(NeonStyle())
Key Takeaways
🚀 The Decorator pattern makes rigid hierarchies flexible. 🔗 SwiftUI modifiers = Built-in decorators! 🛠 Use it to dynamically add behaviors at runtime. 💡 It follows the Open/Closed Principle (open for extension, closed for modification).
What’s Next? The Facade Pattern
In our next article, we’ll dive into the **Facade pattern — your shortcut to simplifying complex libraries and legacy code**.
메타데이터
- post_id
- 4fc18216e2cb
- slug
- decorator-design-patterns-series-part-6-4fc18216e2cb
- url
- https://medium.com/@ios-dev-dilip-kumar/decorator-design-patterns-series-part-6-4fc18216e2cb
- canonical_url
- https://medium.com/@ios-dev-dilip-kumar/decorator-design-patterns-series-part-6-4fc18216e2cb
- author_url
- https://medium.com/@ios-dev-dilip-kumar
- status
- ok
- fetched_at
- 2026-08-09 06:08:05