While Inheritance/Sub Classing seems to be most natural way of extending functionality, it might not be always the best option. Especially when you want to avoid subclassing as it might result in too many classes. Consider the following scenario.
Your nearest Dominos has many varieties of Pizzas. On top of it, you could decorate each of those pizzas with extra toppings and choose from a variety of crusts. If we were go with the conventional sub classing approach, we could end up with a lot of sub classes as seen below.
public interface IPizza { string Description { get; } int CalculatePrice(); } public class MexicanGreenWavePizza : IPizza { public string Description => "Mexican Green Wave Pizza"; public int CalculatePrice() => 105; } public class MargheritaPizza : IPizza { public string Description => "Margherita Pizza"; public int CalculatePrice() => 100; } public class ChickenTikkaPizza : IPizza { public string Description => "Chicken Tikka Pizza"; public int CalculatePrice() => 115; }
We will now add our decorators.
class CheeseCrust : IPizza { private IPizza _PizzaInstance; public CheeseCrust(IPizza pizza) => _PizzaInstance = pizza; public string Description => $"{_PizzaInstance.Description} With Cheese Crust"; public int CalculatePrice() => _PizzaInstance.CalculatePrice() + 7; } public class ExtraGoldenCorn : IPizza { private IPizza _PizzaInstance; public ExtraGoldenCorn(IPizza pizza) => _PizzaInstance = pizza; public string Description => $"{_PizzaInstance.Description} with Extra Golden Corn"; public int CalculatePrice() => _PizzaInstance.CalculatePrice() + 5; }
As you might have already noticed, the decorator classes not just inherit from IPizza interface, but it also have an instance of Pizza with it, injected using Dependency Injection. This allows us to use the decorators with any combination of base classes (and with each other) without having to write too many sub classes as we had observed in the image above. The client can now use any combination of Pizza’s with their decorators.
IPizza vegPizza = new MargheritaPizza(); var vegPizzacheezeCrust = new CheeseCrust(vegPizza); // Added Decorator IPizza nonVegPizza = new ChickenTikkaPizza(); var chickenPizzacheezeCrust = new CheeseCrust(nonVegPizza); // Added Decorator var chickenPizzacheezeCrustAndExtraGoldenCoren = new ExtraGoldenCorn(chickenPizzacheezeCrust); // Added Decorator Console.WriteLine($"{vegPizzacheezeCrust.Description}, Price : {vegPizzacheezeCrust.CalculatePrice()}"); Console.WriteLine($"{chickenPizzacheezeCrustAndExtraGoldenCoren.Description}, Price : {chickenPizzacheezeCrustAndExtraGoldenCoren.CalculatePrice()}");
Output
Margherita Pizza With Cheese Crust, Price : 107 Chicken Tikka Pizza With Cheese Crust with Extra Golden Corn, Price : 127<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
The complete reference source code for the example can be found in my Github. To learn more on Design Patterns, refer here.
One thought on “Design Patterns : Decorator Pattern”