Simplifying Codebases with Swift’s Decorator Design Pattern

What is the Decorator Pattern?

The decorator pattern is a structural design pattern that enables dynamic extension of an object’s behavior without affecting other objects of the same class. It promotes composition over inheritance, leading to more modular and maintainable code.

When to Use the Decorator Pattern

You should use the decorator pattern when:

  • You need to dynamically modify an object’s behavior without affecting other objects of the same class.
  • You want to create variations of an object with different combinations of behaviors without creating complex subclasses.
  • You need to customize components in an application without affecting other parts of the system.
  • You want to avoid the limitations of inheritance, such as tight coupling and inflexibility.

Implementing the Decorator Pattern in Swift

Let’s take a pizza ordering system as an example. We can use the decorator pattern to allow for dynamic modification of the Pizza object’s behavior based on customer choices.

protocol Pizza {
    var description: String { get }
    var cost: Double { get }
}
class CheesePizza: Pizza {
    let description = "Cheese Pizza"
    let cost: Double = 10.99
}

To enable customization, define decorator classes that also conform to the Pizza protocol:

class ToppingDecorator: Pizza {
    let basePizza: Pizza
    let topping: String
    let toppingCost: Double

    init(basePizza: Pizza, topping: String, toppingCost: Double) {
        self.basePizza = basePizza
        self.topping = topping
        self.toppingCost = toppingCost
    }

    var description: String {
        return basePizza.description + ", " + topping
    }

    var cost: Double {
        return basePizza.cost + toppingCost
    }
}

To use the decorator pattern, initialize the concrete base Pizza object and wrap it in one or more decorator objects:

let cheesePizza = CheesePizza()
let pepperoniPizza = ToppingDecorator(basePizza: cheesePizza, topping: "Pepperoni", toppingCost: 1.99)
let mushroomPizza = ToppingDecorator(basePizza: pepperoniPizza, topping: "Mushroom", toppingCost: 0.99)

print(mushroomPizza.description) // Output: Cheese Pizza, Pepperoni, Mushroom
print(mushroomPizza.cost) // Output: 13.97

Benefits of the Decorator Pattern

The decorator pattern offers several benefits:

  • Modularity: Each decorator class is responsible for a single concern, making it easier to understand and maintain code.
  • Flexibility: You can add new behaviors to an object without affecting other objects of the same class.
  • Scalability: The decorator pattern enables you to extend your codebase in a scalable way.

Leave a Reply