How to work with the Decorator design pattern in C#

Take advantage of the Decorator design pattern to add extensibility either dynamically or statically to your application

Decorator

Decorator

Design patterns are solutions to recurring problems and complexities in software design and are classified into three distinct categories: creational, structural, and behavioral.

The Decorator design pattern is a structural pattern and can be used to add functionality to an object dynamically sans the need of modifying the structure of the object. In essence, you can leverage the Decorator pattern to attach functionality or behavior to an object dynamically or statically sans the need to altering the object's structure.

Note that the Decorator design pattern follows the Open Closed Principle, one of the SOLID principles. Incidentally, the Open Closed Principle is used to design classes that are open for extensions but closed for modifications. Conformance to the Open Closed Principle facilitates building applications that are reusable and can be maintained easily. The Gang of Four (GOF) at Dofactory states: "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."

A bit of code

In this section we will explore how we can implement the Decorator design pattern in C#. The participants in a typical implementation of the Decorator design pattern include:

  1.  Component -- this represents the base type of the actual or the concrete type
  2. Concrete Component -- this represents the concrete type that extends the base component. Note that the additional responsibilities or functionalities are added in this type.
  3. Decorator -- this represents a reference to a component. The dynamic functionalities are added in this type.

Now, let's consider the following class.

public abstract class Employee

   {

       public abstract string Display();

   }

Note that when using the Decorator design pattern, you extend the behavior of an existing class but it doesn't necessarily mean that you have to use abstract types -- the types may or may not be abstract. You can also implement the Decorator design pattern using interfaces, or even by using methods that are virtual in your concrete classes. In essence, you are not constrained to use only abstract classes when implementing the Decorator design pattern. We are using an abstract class here just for the sake of simplicity.

The EmployeeConcrete class extends the Employee class and adds additional properties to it. Here's how this class would look like.

   public class EmployeeConcrete : Employee

   {

       public string FirstName { set; get; }

       public string LastName { set; get; }

       public string Address { set; get; }

       public override string Display()

       {

           StringBuilder data = new StringBuilder();

           data.Append("First name: " + FirstName);

            data.Append("\nLast name: " + LastName);

           data.Append("\nAddress: " + Address);

           return data.ToString();

       }

   }

The EmployeeDecorator class extends the Employee class, accepts an instance of the component class named Employee, and overrides the Display() method. Here's how this class would look like.

public class EmployeeDecorator : Employee

   {

       Employee employee = null;

       protected EmployeeDecorator(Employee employee)

       {

           this.employee = employee;

       }

       public override string Display()

       {

           return employee.Display();

       }

   }

Now that the component, concrete component and the decorator class is ready, you can now extend the EmployeeDecorator class to create a concrete decorator class. The following code listing shows how this class would look like.

public class PermanentEmployeeDecorator : EmployeeDecorator

   {

       //Add properties relevant to a permanent employee

       private double PF { get; set; }

       public PermanentEmployeeDecorator(Employee employee) : base(employee)

       {   }

       public override string Display()

       {

           return base.Display() + "\nEmployee type: Permanent";

       }

   }

And, that's all you need to do! You can now create an instance of PermanentEmployeeDecorator and use it as shown in the code snippet below.

static void Main(string[] args)

       {

           EmployeeConcrete employeeConcrete = new EmployeeConcrete

         { FirstName = "Joydip", LastName = "Kanjilal", Address = "Hyderabad, India" };

           PermanentEmployeeDecorator employeeDecorator = new PermanentEmployeeDecorator(employeeConcrete);

           Console.WriteLine(employeeDecorator.Display());

           Console.Read();

       }

You can also have another type of employee -- a contractual employee. To represent it, you would need to create another class named ContractEmployeeDecorator that extends the EmployeeDecorator class. Refer to the code snippet given below.

public class ContractEmployeeDecorator : EmployeeDecorator

   {

       //Add properties relevant to a contract employee

       private double RatePerHour { get; set; }

       public ContractEmployeeDecorator(Employee employee) : base(employee)

       { }

       public override string Display()

       {

           return base.Display() + "\nEmployee type: Contractual";

       }

   }

The following code snippet illustrates how you can use the ContractEmployeeDecorator class.

static void Main(string[] args)

       {

           EmployeeConcrete employeeConcrete = new EmployeeConcrete

{ FirstName = "Joydip", LastName = "Kanjilal", Address = "Hyderabad, India" };

           ContractEmployeeDecorator employeeDecorator = new ContractEmployeeDecorator(employeeConcrete);

           Console.WriteLine(employeeDecorator.Display());

           Console.Read();

       }

This article is published as part of the IDG Contributor Network. Want to Join?

From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.