Greetings, traveler!
Let’s continue our discussion on Structural Design Patterns. Today, we will explore the Bridge pattern. The Bridge pattern divides a complex system into two hierarchies—abstraction and implementation—that can be developed separately. This allows for more flexibility and easier maintenance of the system.
Problem
The Bridge pattern addresses a common issue that programmers encounter — the so-called “exploding class hierarchy.” This occurs when the number of classes in a system exponentially grows as new functions are added.
For example, let’s say you have a “Car” class. To create new variations, you could create a “Hatchback” and “Station Wagon” subclasses. Then, to add color options, you would make additional classes such as “Red Hatchback” and “Red Station Wagon,” “Green Hatchback,” and “Green Station Wagon.” After that, you might want to add a “Sedan” option, requiring even more subclasses to be created.
And here, the Bridge Pattern comes to the rescue. There are four participants in this story:
- Abstraction
- Refined Abstraction
- Implementation Interaface
- Concrete Implementation
Example of usage
Let’s go straight to the live example. Imagine that we have a task — to cook dinner. We have food and a frying pan for that. We’ll write the code right away.
// Abstraction
protocol FryingPanProtocol {
var meal: MealProtocol { get set }
func cook()
}
// Implementation Interface
protocol MealProtocol {
func cook()
}
// Refined Abstraction
final class FryingPan: FryingPanProtocol {
var meal: MealProtocol
init(meal: MealProtocol) {
self.meal = meal
}
func cook() {
meal.cook()
}
}
// Concrete Implementation
final class FriedEggs: MealProtocol {
func cook() {
print("The eggs are cooked")
}
}
// Concrete Implementation
final class RoastBeef: MealProtocol {
func cook() {
print("The roast beef is cooked")
}
}
The Frying Pan acts as the bridge between the client and the meal, helping to cook any desired dinner.
// Client
final class DinnerManager {
let roastBeef = RoastBeef()
let friedEggs = FriedEggs()
lazy var panForBeef = FryingPan(meal: roastBeef)
lazy var panForEggs = FryingPan(meal: friedEggs)
func serveDinner() {
[panForBeef, panForEggs].forEach {
$0.cook()
}
}
}
Conclusion
The Bridge Design Pattern helps hide implementation details from the client and implements the open-closed principle. It makes it possible to manage different modules independently.
And that’s all I wanted to tell you about this pattern today. Now, let’s move on to the next Structural Design Pattern — the Composite Pattern.
Check out other posts in the Design Patterns series:
- Visitor Design Pattern in Swift
- Themplate Method Design Pattern in Swift
- Strategy Design Pattern in Swift
- State Design Pattern in Swift
- Observer Design Pattern in Swift
- Memento Design Pattern in Swift
- Mediator Design Pattern in Swift
- Iterator Design Pattern in Swift
- Command Design Pattern in Swift
- Chain of Responsibility Design Pattern in Swift
- Proxy Design Pattern in Swift
- FlyWeight Design Pattern in Swift
- Facade Design Pattern in Swift
- Decorator Design Pattern in Swift
- Composite Design Pattern in Swift
- Adapter Design Pattern in Swift
- Singleton Design Pattern in Swift
- Prototype Design Pattern in Swift
- Builder Design Pattern in Swift
- Abstract Factory Design Pattern in Swift
- Factory Method Design Pattern in Swift
- Design Patterns: Basics