Click here to Skip to main content
15,892,674 members
Articles / Programming Languages / C#
Tip/Trick

Decorator Design Pattern

Rate me:
Please Sign up or sign in to vote.
3.00/5 (4 votes)
17 Jan 2016CPOL2 min read 6.8K   2   2
Here, I will explain the real world example of decorator pattern so it will be easier for readers to implement this pattern in their application.

Introduction

This tip will talk about the decorator pattern and its use in real world applications.

What is Decorator Design Pattern

Decorator pattern attaches additional responsibilities to the object at runtime. Decorator provides flexible alternative to subclassing for extending functionality. Basically, we use this pattern when the basic functionality for the class is already implemented/tested, but we want to provide some additional functionality (depends on requirement change) along with the basic one. While adding new functionality, we don’t want to make changes in the existing classes as it is well tested and we don’t want to violate Open-Closed principle.

Using the Code

Now, let’s take a look at the example. Here, we are working in Data Access Layer (DAL). We are basically performing CRUD (Create, read, update and delete) operation on Person Entity. We can basically fetch/update Person entity from two repositories, either SQL repository or WebAPI repository. To implement this change, we will create IDataAccess interface as below:

C#
public interface IDataAccess
{
    bool CreatePerson(Person person);
    bool UpdatePerson(int id, Person person);
    bool DeletePerson(int id);
    Person SearchPerson(int id);
}

Now, we will implement our two repositories, SQLRepository and WebAPIRepository as below.

C#
public class SQLDataAccess : IDataAccess
{
    public bool CreatePerson(Person person)
    {
        // SQL Server implementation
    }

    public bool UpdatePerson(int id, Person person)
    {
        // SQL Server implementation
    }

    public bool DeletePerson(int id)
    {
        // SQL Server implementation
    }

    public Person SearchPerson(int id)
    {
        // SQL Server implementation
    }
}

public class WebAPIDataAccess : IDataAccess
{
    public bool CreatePerson(Person person)
    {
       // Web API implementation
    }

    public bool UpdatePerson(int id, Person person)
    {
        // Web API implementation
    }

    public bool DeletePerson(int id)
    {
        // Web API implementation
    }

    public Person SearchPerson(int id)
    {
        // Web API implementation
    }
}

I haven't provided any implementation for both of the classes as it is not important here. Now, let's imagine our above SQL and WebAPI implementations are working fine and well tested. Now, we have requirement change request for our existing implementation. We want to support caching as SearchPerson method is taking time for returning the result.

Now, how we will support caching in our existing implementation. Possible solution (without implementing decorator pattern) is to directly support caching in both repositories, but the disadvantages with this approach are as follows:

  1. It will violate DRY (Do not Repeat Yourself) principle since we will be implementing the same code at two different locations. Also, if we want to change our caching logic, we need to make changes in both the classes.
  2. It will violate single responsibility group. Since SQLDataAccess and WebAPIDataAccess both should only support fetching data from respective repository. Not any other thing.
  3. It may break our existing functionality, so we might need to test everything from scratch.

Now, here the decorator pattern will come to our rescue. :):)

How we will create new class MemoryCacheDataAccess derived from IDataAccess is shown below:

C#
public class MemoryCacheDataAccess : IDataAccess
{
    private readonly IDataAccess _dataAccess;
    private Dictionary<int, Person> _memoryCache = new Dictionary<int, Person>();

    public MemoryCacheDataAccess(IDataAccess dataAccess)
    {
        // Here to the constructor we are passing IDataAccess interface
        _dataAccess = dataAccess;
    }

    public bool CreatePerson(Person person)
    {
        var isCreated = _dataAccess.CreatePerson(person);
        if (isCreated)
            _memoryCache.Add(person.Id, person);

        return isCreated;
    }

    public bool UpdatePerson(int id, Person person)
    {
        var isUpdated = _dataAccess.UpdatePerson(id, person);
        if (isUpdated)
        {
            if(_memoryCache.ContainsKey(person.Id))
            {
                _memoryCache[person.Id] = person;
            }
            else
            {
                _memoryCache.Add(person.Id, person);
            }
        }

        return isUpdated;
    }

    public bool DeletePerson(int id)
    {
        var isDeleted = _dataAccess.DeletePerson(id);

        if(isDeleted)
        {
            if (_memoryCache.ContainsKey(person.Id))
            {
                _memoryCache.Remove(id);
            }
        }

        return isDeleted;
    }

    public List<Person> SearchPerson(int id)
    {
        if (_memoryCache.ContainsKey(id))
            return new List<Person> { _memoryCache[id] };

        return _dataAccess.SearchPerson(id);
    }
}

Now, the interesting thing about this class is that we are passing IDataAccess interface reference to the constructor. So we can easily create wrapper around SQLDataAccess or WebAPIDataAccess. For all the operations, we will be calling actual dataaccess method, once result is back from the actual repository, we are storing this into our cache. The actual code invocation is as below:

C#
static void Main(string[] args)
{
    IDataAccess sqlDataAccess = new SQLDataAccess();
    IDataAccess cacheDataAccess = new MemoryCacheDataAccess(sqlDataAccess);
}

So in this example, we have attached additional responsibility (i.e., caching support) to our dataaccess without making changes in the actual classes. In the similar fashion, we can add more wrappers around our existing dataaccess classes like ExceptionHandlerDataAccess, PerformanceMonitorDataAccess, etc. Invocation for this wrapper will be as below:

C#
static void Main(string[] args)
{
    IDataAccess sqlDataAccess = new SQLDataAccess();
    IDataAccess cacheDataAccess = new MemoryCacheDataAccess(sqlDataAccess);
    IDataAccess exceptionHandlerDataAccess = new ExceptionHandlerDataAccess(cacheDataAccess);
}

I hope you have understood the decorator design pattern. Please share your feedback in the comments below.

History

  • 17th January, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Citi bank
India India
I am currently working in citibank as programmer analyst. I have almost 7 years of experience in IT. I worked mainly on C#, WPF and WCF.
This is a Social Group (No members)


Comments and Discussions

 
PraiseNicely Explained !!!! Pin
Member 1234534422-Feb-16 21:34
Member 1234534422-Feb-16 21:34 
GeneralDRY Principle Pin
Member 1226975417-Jan-16 13:40
Member 1226975417-Jan-16 13:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.