Decorator pattern adds behavior to objects without affecting other objects of the same class. The decorator pattern is a useful alternative to creating sub-classes.
Create a module for each decorator. This approach is more flexible than inheritance because you can mix and match responsibilities in more combinations. Additionally, because the transparency allows decorators to be nested recursively, it allows for an unlimited number of responsibilities.
Assume the Pizza class has a cost method that returns 300:
class Pizza
def cost
300
end
end
Represent pizza with an added layer of cheese burst and the cost goes up by 50. The simplest approach is to create a PizzaWithCheese
subclass that returns 350 in the cost method.
class PizzaWithCheese < Pizza
def cost
350
end
end
Next, we need to represent a large pizza that adds 100 to the cost of a normal pizza. We can represent this using a LargePizza subclass of Pizza.
class LargePizza < Pizza
def cost
400
end
end
We could also have an ExtraLargePizza which adds a further cost of 15 to our LargePizza. If we were to consider that these pizza types could be served with cheese, we would need to add LargePizzaWithChese and ExtraLargePizzaWithCheese subclasses.we end up with a total of 6 classes.
To simplify the approach, use modules to dynamically add behavior to Pizza class:
Module + extend + super decorator:->
class Pizza
def cost
300
end
end
module CheesePizza
def cost
super + 50
end
end
module LargePizza
def cost
super + 100
end
end
pizza = Pizza.new #=> cost = 300
pizza.extend(CheesePizza) #=> cost = 350
pizza.extend(LargePizza) #=> cost = 450
pizza.cost #=> cost = 450