Click here to Skip to main content
Click here to Skip to main content

The Decorator Pattern - Learning with Shapes

, 4 Apr 2011
Rate this:
Please Sign up or sign in to vote.
Give your objects extra functionality dynamically

Introduction

There’s a pattern in everything you ever come across in your life, so why not learn and get inspired from them! Here, I am adopting a different approach to present a dry topic for most of the programmers, i.e., learning design patterns, but taking a little help from nature and the things around us, i.e., shapes, which we tend to remember most often than the theory itself.

Background

There’s a concept most of you might already be familiar with, i.e., SOLID but for those who don’t know, it stands for Single Responsibility, Open/Closed Principle, Liskov substitution, Interface Segregation and Dependency Inversion, introduced by Robert C. Martin, which is basically a collection of principles that one should keep in mind while creating a system that’s easy to maintain and extend over time.

I am going to target its Open/Closed Principle through Decorator Pattern. Simply put, your application’s architecture should be open for extension but closed for modification. Let’s have a look at the approach Decorator pattern has adopted to address this problem.

The Problem

Before we dive directly into the implementation of this pattern, let’s first look at the problems it solve, so that we can really appreciate its existence.

Suppose you are hired to provide consultancy to solve a challenging problem facing by a particular firm. They have an old system having lots of business objects managing complex business flows.

Now here are the requirements. They want extra functionality to be implemented on these objects on demand, i.e., dynamically and to only the ones they want!

Let’s have a look at their current system and how they’ve tried to cater this problem:

Current System

Fig 1: Current Design

As you can see, their current implementation uses inheritance as their architecture's flexibility and extension technique. Now, they are trying to give these business objects the functionality of sending Mail, SMS and Fax with relevant details. They are trying to extend this architecture like this:

Fig 2: Proposed design

There are some problems with this approach, we can say this right by looking at the class explosion because they are violating a very important principle, yes the one for which decorator pattern offers its services.

Problems with this Approach

They have just created a maintenance nightmare for them by introducing so many classes within their architecture. For example, what will happen when they want to alter their Fax send logic? They have to change it in each and every object that intends to provide Fax sending services.

If in the future they want to integrate a new business object into this architecture, they not only have to provide the concrete implementation of their new business object but also the implementations of any other service they want their new object to provide.

Now, we can say this for sure that this architecture is not closed for modification because if they want to change their logic related with any new functionality provided by the business objects, they have to open all of the classes providing those services and make the desired changes.

Problems with Inheritance

Before we learn how to implement the pattern, let’s first discuss what would be the disadvantages of inheritance based approach and is there any other better alternative to use instead. I’ll more specifically be talking about concrete inheritance here, i.e., classes extends a base class to inherit some functionality from it, not the interface inheritance.

In inheritance, the slightest change in the base class will break the code, i.e., if you have just changed the return type of a method overridden in the sub-classes, e.g., you have a class named baseBusinessObject which has a virtual method named PerformTransaction() which returns int, i.e., any non-zero value represents success scenario and zero equal to failed transaction. In the future, if you have changed the return type to bool for the sake of readability, all of the sub-classes will eventually break their code.

When we think inheritance, we always think of “Is-a” relation e.g. Ferrari “Is-a” Car, But this implementation won’t sounds logical in some situations, for example, Engineer is-a employee but what if that engineer becomes manager? You then again have to change your implementation logic to cope with this requirement, these are the situations that we must keep in mind while designing our architecture.

Meet Composition

Another alternative approach or competitor to inheritance is composition. It’s like encapsulating a functionality into its own box, and when you want to fight with new requirements, your maintenance effort will be applied only at that particular functionality, the remaining part of the system will be untouched.

Let me explain it with an example:

class Paul : Fighter
{
    //implementation
}
class PaulWithSuperCombo : Paul
{
    pubic void PerformSuperCombo() { }
    //other implementation
}

Suppose you have a class named ‘PaulWithSuperCombo’ that extends ‘Paul’, what if you came to know that both ‘King’ and ‘Kuma’ can also perform super combo? How will you incorporate the change in your architecture? You are indeed in trouble. Here, composition has a competitive edge on inheritance. For example:

class SuperCombo
{
    public void PerformSuperCombo() { }
}

class King : Fighter
{
    public SuperCombo ComboFunctionality = new SuperCombo();
}

class Kuma : Fighter
{
    public SuperCombo ComboFunctionality = new SuperCombo();
}

Here we have composed the Super Combo Functionality within its own class and ‘King’ and ‘Kuma’ can use this functionality now, i.e.:

King king = new King();
king.ComboFunctionality.PerformSuperCombo();
Kuma kuma = new Kuma();
kuma.ComboFunctionality.PerformSuperCombo();

By using this approach, we’ve restricted our maintenance effort in one particular class as far as Super Combo functionality is concerned, no matter how many classes have this implementation, if we want to make any modifications to it, we know where to look for it.

Back to the Problem

Now let’s get back to the problem we were facing in Fig: 1. We’ll now try to cater to this problem by using composition as we have seen it’s a better approach than inheritance.

Meet the Decorator Pattern

Decorator Pattern complements composition and gives your objects extra functionality dynamically, i.e., at run-time. Think of Decorator as the wrapper around your object. For example, take the onion as the implementation of decorator pattern provided by nature, i.e.:

Fig 3: Decorator Pattern’s Implementation by Nature

Let’s see how we are actually going to implement the pattern:

interface IDecorator
{
    string Description();
}

To keep things simple, let’s start off with a simple interface that has a single method ‘Description()’ in it. Every object that intends to have some extra functionality at run-time must implement this interface and the extra functionality, i.e., our Decorator must also implement this interface so that we can achieve polymorphism, this is what the beauty of polymorphism is which lets us dynamically change the concrete binding.

Our base class baseBusinessObject must implement this interface so that all of the sub-classes have a reference to it and can be type casted to our IDecorator interface.

abstract class baseBusinessObject : IDecorator
{
    public string Description()
    {
        return "Business Object";
    }
    //other implementation common to business objects
}

and here come our decorators that will decorate any object that extends baseBusinessObject, first comes our Mail decorator that will give Mail functionality to any business object that intends to have it dynamically, i.e.:

class MailDecorator : IDecorator
{
    //receiving object
    IDecorator _obj;
    public MailDecorator(IDecorator obj)
    {
        _obj = obj;
    }
    public string Description()
    {
        return _obj.Description() + " With Mail Functionality";
    }
    //other implementation
}

Now, what we have done here? We have composed a reference to object that implements IDecorator interface, i.e., IDecorator _obj, so that, we can receive the instance of this object at runtime and then dynamically assign extra responsibilities to it. We can use this decorator like:

//main object
BusinessObject BusinessObj = new BusinessObject();
//give BusinessObj mail functionality
IDecorator BusinessObjWithMail = new MailDecorator(BusinessObj);
Console.WriteLine(BusinessObjWithMail.Description());

This will certainly print:

“Business Object With Mail Functionality” 

And here’s the SMS Decorator:

class SMSDecorator : IDecorator
{
    //receiving object
    IDecorator _obj;
    public SMSDecorator(IDecorator obj)
    {
       _obj = obj;
    }
    public string Description()
    {
        return _obj.Description() + " With SMS Functionality";
    }
    //other implementation
}

As you’ve tested the pattern, now you know how to make the last one, i.e., Fax Decorator. Now, here comes the interesting part, memorizing this pattern with shapes. You’ve already seen one, i.e., Fig 3, but I like this one:

Fig 4: Series of Mountains depict our Decorator Pattern

As you can see, by looking at the above scenery in terms of Decorator Pattern, decorators, i.e., series of mountains, enhances the beauty of our main Object, i.e., the primary mountain view Smile | :) . I can never forget the beauty nature has to offer and so the pattern now.

More Convenience

We have seen one way of decorating our object with our decorator, but there is a more convenient way one can choose to apply decorators, i.e.:

IDecorator enhancedBusinessObject = new MailDecorator(
       new SMSDecorator(
       new FaxDecorator(BusinessObj)));

You can decorate your object with as many decorators as you want, but you should also be careful as the inappropriate use of this pattern will introduce complexity in your architecture.

Definition

You are in a perfect position now to understand the definition:

“The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.”

and you know what it means now!

Conclusion

Decorators enhance the functionality of your objects dynamically, but you should also take extra precautions while using a lot of them in your architecture because it’ll overcomplicate things.

History

  • 4th April, 2011: Initial post

License

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

About the Author

AliAmjad
Software Developer
Pakistan Pakistan
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmembercotopboy Zhang11-Apr-11 22:31 
GeneralRe: My vote of 5 PinmemberAliAmjad12-Apr-11 8:39 
GeneralMy vote of 5 Pinmember00000000000000000000011-Apr-11 4:34 
GeneralRe: My vote of 5 PinmemberAliAmjad12-Apr-11 8:38 
GeneralMy vote of 5 PinmemberMathiyazhagan5-Apr-11 3:13 
GeneralRe: My vote of 5 PinmemberAliAmjad5-Apr-11 19:13 
GeneralYou should pick other images to show the decorator pattern PinmemberJV99995-Apr-11 2:19 
GeneralRe: You should pick other images to show the decorator pattern PinmemberThiago R. Santos16-Jan-12 11:09 
GeneralThe decorator pattern PinmemberDaveAuld4-Apr-11 23:15 
GeneralRe: The decorator pattern PinmemberAliAmjad5-Apr-11 0:27 
GeneralMy vote of 5 PinmemberPetr Pechovic4-Apr-11 22:46 
GeneralRe: My vote of 5 PinmemberAliAmjad5-Apr-11 0:26 
GeneralMy vote of 5 PinmemberRemi BOURGAREL4-Apr-11 22:21 
GeneralRe: My vote of 5 PinmemberAliAmjad5-Apr-11 0:25 
GeneralMy vote of 5 PinmemberNaerling4-Apr-11 11:06 
GeneralRe: My vote of 5 PinmemberAliAmjad4-Apr-11 21:38 
GeneralMy vote of 5 PinmvpPete O'Hanlon4-Apr-11 9:48 
GeneralRe: My vote of 5 PinmemberAliAmjad4-Apr-11 9:55 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 4 Apr 2011
Article Copyright 2011 by AliAmjad
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid