Lukasz Welna
  • Home
  • Portfolio
  • About me
  • Articles
  • Contact
28 December 2023 by LukaszWelna
Programming, Design patterns

Decorator

Decorator
28 December 2023 by LukaszWelna
Programming, Design patterns

Introduction

The Decorator pattern is a member of the structural design patterns. It allows dynamic addition of new functionalities to objects at runtime by encapsulating the base object within specialized objects called decorators. Decorators extend behavior of the base object without changing its structure. 

Through the use of composition, each decorator dynamically adds a specific functionalities to the base object, enhancing flexibility and modularity.

Problem

Imagine a coffee bar offering two standard black coffees available in two sizes: normal and large. Customers can customize their coffee orders by adding ingredients like sugar, milk, and cream in varying amounts. How can we handle this flexibility? 

The first thought might be to use inheritance to extend base coffee classes. However, inheritance is static, locking the behavior of objects at compile time. This approach demands creating numerous subclasses for all potential combinations, leading to complexity and a lack of scalability. Additionally, any future addition of new ingredients would require new classes, making this solution short-sighted.

Another, better approach is to use Decorator pattern. This pattern enables the addition of new features to existing objects dynamically at runtime. With decorators representing each ingredient, the system becomes more adaptable and modular. Adding a new ingredient in the future requires implementing only a new decorator, ensuring scalability and ease of modification.

Structure

ICoffee: Coffee interface. It declares the interface, which is implemented by both concrete components and the base decorator. 

Normal coffee, Large coffee: Concrete components. They implement the ICoffee interface and provide the core functionalities of different coffee sizes.

BaseCoffeeDecorator: Abstract class, base decorator. It implements the ICoffee interface and additionally maintains a reference to the wrapped object, which can either be a base object or another decorator. 

Sugar decorator etc.: Concrete decorators. They extend base decorator and add new features to the objects by wrapping them. 

Implementation

 //C# code

 // Coffee interface
 internal interface ICoffee
 {
     public double CalculatePrice();
 }

 // Concrete base coffees
 internal class NormalCoffee : ICoffee
 {
     public double CalculatePrice()
     {
         return 5;
     }
 }

 internal class LargeCoffee : ICoffee
 {
     public double CalculatePrice()
     {
         return 10;
     }
 }

 // Base decorator
 internal abstract class BaseCoffeeDecorator : ICoffee
 {
     private ICoffee _coffee;

     protected BaseCoffeeDecorator(ICoffee coffee)
     {
         _coffee = coffee;
     }

     public virtual double CalculatePrice() 
     { 
         return _coffee.CalculatePrice();
     }
 }

 // Concrete decorators
 internal class SugarDecorator : BaseCoffeeDecorator
 {
     public SugarDecorator(ICoffee coffee) : base(coffee) { }

     public override double CalculatePrice()
     {
         return base.CalculatePrice() + 2;
     }
 }

 internal class MilkDecorator : BaseCoffeeDecorator
 {
     public MilkDecorator(ICoffee coffee) : base(coffee) { }

     public override double CalculatePrice()
     {
         return base.CalculatePrice() + 3;
     }
 }

 internal class CreamDecorator : BaseCoffeeDecorator
 {
     public CreamDecorator(ICoffee coffee) : base(coffee) { }

     public override double CalculatePrice()
     {
         return base.CalculatePrice() + 5;
     }
 }

 internal class Program
 {
     static void Main(string[] args)
     {
         // Create normal coffee object
         var normalCoffee = new NormalCoffee();
         Console.WriteLine($"Coffee price: {normalCoffee.CalculatePrice()}");

         // Add sugar to normal coffee
         var normalCoffeeWithSugar = new SugarDecorator(normalCoffee);
         Console.WriteLine($"Coffee price: {normalCoffeeWithSugar.CalculatePrice()}");

         // Add milk to normal coffee with sugar
         var normalCoffeeWithSugarAndMilk = new MilkDecorator(normalCoffeeWithSugar);
         Console.WriteLine($"Coffee price: {normalCoffeeWithSugarAndMilk.CalculatePrice()}");
     }
 }
Result:
Coffee price: 5 PLN
Coffee price: 7 PLN
Coffee price: 10 PLN

Conclusion

The client (Main method) creates a base object and then decides which functionalities should be added to this object. This enhancement of features occurs at runtime, offering enhanced flexibility. The wrapped object can be either the base object or another decorator, since both concrete components and decorators implement the ICoffee interface. 

The Decorator pattern should be utilized when the objects need the ability to dynamically extend their functionalities at runtime.

Decorator pattern aligns with:

  • Single Responsibility Principle: Decorators focus on enhancing specific functionalities of the objects. 
  •  Open/Closed Principle: New features can be added by implementing new decorator classes, enhancing extensibility without requiring modifications to the existing code.  
Previous articleObserverNext article SOLID Principles

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

Stub vs Mock21 March 2024
Test Driven Development18 March 2024
SOLID Principles31 December 2023

Categories

  • Programming (6)
  • Design patterns (3)
  • Tests (2)

Archives

  • March 2024 (2)
  • December 2023 (4)

About me

Hello! My name is Lukasz. I am a mechatronic engineer by profession. My passion is a programming. Every day I improve my skills in this field, because I want to be a web developer. I encourage You to check my portfolio and to contact with me in case of questions.

Contact

+48 535 174 992
lukasz.welna96@gmail.com

Recent Posts

Stub vs Mock21 March 2024
Test Driven Development18 March 2024
SOLID Principles31 December 2023
Copyright © 2025 Lukasz Welna. All rights reserved.

Recent Posts

Stub vs Mock21 March 2024
Test Driven Development18 March 2024
SOLID Principles31 December 2023

Categories

  • Programming
  • Design patterns
  • Tests

Archives

  • March 2024
  • December 2023