Decorator design pattern for beginners.

Background 

In the existing software system. How to add a new functionality at run time?

Let’s understand it through an example, we have a text editor class. Who facilitates us write/edit/modify text. As per standard, we should have 80 characters width and 100 lines per page. Again that depends upon the individual user editor’s setting.

Now, what if the user wrote more content, and cross the page size limit? Typically a user can start writing on a new page, and he can go to the previous page through arrow key (^) present in keyboard. 

User is looking for vertical and horizontal scroll bars to appear on the page. So that, instead of using arrow key(^present in the keyboard, users can use vertical and horizontal scroll bars (graphically) and navigate pages easily.

As we understood correctly, vertical and horizontal scroll bars will appear on run time. It will not be there from the starting. It will appear only when we cross-page size limits (vertical or horizontal).

There has to be a component in my software that will decorate my text editor. When I needed it at run time. We could have inserted vertical and horizontal scroll bars logic in text editor class. But before adding, we have to ask one question, is that a core behavior of text editor class? 

The core behavior of text editor class is to facilitate a page where user can write/delete/modify the content. Not to provide fancy graphical tools on editor. 

Remember one thing, you will encounter many features around your core business logic class. That doesn’t mean you start implementing all logic inside your single-core class. Until and unless it is a core behavior of that class. If you do so, that class will become unmanageable, and it will become buggiest code in your code repository. Make sure you follow all OOP principles( single responsibility and open-close principles in this case).

Now think you have found 10 different features. Which can be added to your software. Those 10 features can appear in any combination. For example, feature 1 alone, feature 1 with feature 5, or future 3, 4, 6, and 9 together so on. Then you will have factorial 10 (!10) combinations to implement them in your code. If you don’t apply proper design, then you have to write !10 different if-else conditions. To validate all individual case and use a specific combination of features on your text editor page at run time.

With decorator design pattern, we can do this by just implementing 10 different classes. In that way, we will save ourselves from writing thousands of lines of codes. We will learn how to apply it, in just a while at below mention code section.

Why we need decorator pattern

We will use this design pattern when we have a big requirement of changing the existing system without affecting its core logic.

We have to understand two ways of changing the existing system,
  1. Invasive, where we change the internal system to implement new behavior.
  2. Evasive, without changing the internal system, make the changes. That is call magic! And that is what decorator dose for you.


About decorator design pattern 

We can add new functionality at run time without affecting the existing code. It is like a wrapper we put on the current object, which has pre and post-processing code in it. If we see any condition to decorate our existing object. In our pre and post-processing code, then we will add extra functionality at run time. It is so easy to use and solves very complex problems in much managed and easy way.

UML Structure 



Example 



Cpp Code 



#include<iostream>
using namespace std;
class TextEditor
{
    public:
        virtual void display() = 0;
        virtual int getLengthOfThePage() {};
        virtual int getWidthOfThePage() {};
        virtual int getCurrentSizeOfThePage() {};
        virtual int getCurrentWidthOfThePage() {};
};

// Class TextEditorImpl is an concreet implementation class of TextEditor interface class.
class TextEditorImpl : public TextEditor
{
    public:
        TextEditorImpl(int maxLenOfRow, int maxLenOfCol, int currentSizeOfThePage = 100,
                        int currentWidthOfThePage = 80)
        :m_maxLenOfRow(maxLenOfRow),
         m_maxLenOfCol(maxLenOfCol),
         m_currentSizeOfThePage(currentSizeOfThePage),
         m_currentWidthOfThePage(currentWidthOfThePage)
         {}
        void display()
        {
            cout<<"Show a test editor page "<<endl;
        }
        
        int getLengthOfThePage()
        {
            return m_maxLenOfRow;
        }
        
        int getWidthOfThePage()
        {
            return m_maxLenOfCol;
        }
        
        int getCurrentSizeOfThePage()
        {
            return m_currentSizeOfThePage;
        }
        
        int getCurrentWidthOfThePage()
        {
            return m_currentWidthOfThePage;
        }
    private:
        // this will show the maximum length edited by user. 
        int m_maxLenOfRow;
        // this will show the maximum width of a line among all lines edited by user. 
        int m_maxLenOfCol;
        int m_currentSizeOfThePage;
        int m_currentWidthOfThePage;
};

class Decorator : public TextEditor
{
    public:
        Decorator(TextEditor *textEditor):m_textEditor(textEditor) {}
        
        void display()
        {
            m_textEditor->display();
        }
    protected:
        TextEditor *m_textEditor ;
};

class VerticalScrollBar : public Decorator
{
    public:
        VerticalScrollBar(TextEditor *textEditor):Decorator(textEditor) {}
        
        void display()
        {
            m_textEditor->display();
            if( m_textEditor->getLengthOfThePage() < m_textEditor->getCurrentSizeOfThePage())
            {
                cout<<"Show Vertical Scroll Bar"<<endl;
            }
        }
};

class HorizontalScrollBar : public Decorator
{
    public:
        HorizontalScrollBar(TextEditor *textEditor):Decorator(textEditor) {}
        
        void display()
        {
            m_textEditor->display();
            if( m_textEditor->getWidthOfThePage() < m_textEditor->getCurrentWidthOfThePage())
            {
                cout<<"Show Horizontal Scroll Bar"<<endl;
            }
        }
};
/*
#######################################################################################
#########  __  __       _        ######################################################
######### |  \/  | __ _(_)_ __   ######################################################
######### | |\/| |/ _` | | '_ \  ######################################################
######### | |  | | (_| | | | | | ######################################################
######### |_|  |_|\__,_|_|_| |_| ######################################################
#######################################################################################
*/
int main(int argc, char **argv)
{
//    TextEditorImpl textEditorImpl01(90,180);
    TextEditor *textEditor01 =  new HorizontalScrollBar(new VerticalScrollBar 
                                (new TextEditorImpl(90,180)));
    textEditor01->display();
//    TextEditor *textEditor02 = new HorizontalScrollBar(new VerticalScrollBar new(TextEditorImpl(60,80)));
    return 0;
}

///////////////////////////////////////////////////////////////////-------------OutPut
/*
Show a test editor page
Show Vertical Scroll Bar
Show Horizontal Scroll Bar
*/


Example 02 -

See the description in CPP code's header below.


/**************************************************************************************
/* Decorator means, decorate your base object. the core logoi or bahaviour of your base
/* object will be as it is, just you are adding new things to it.
    
   for example -
    Many times we invite our friends and family to show out home sweet home, so we decorate 
    our home, but the home will be same. For different occasion we decorate our home 
    differently, like for Birthdays it is different from candle light dinner, or from 
    puja or diwali it will be different. 
    So depending upon occasion we decorate our base object (Home :))
    
***************************************************************************************/

#include<iostream>
using namespace std;
class HomeShow
{
    public:
        virtual void homeShowToVisitors() = 0;
};

class HomeShowImple : public HomeShow
{
    public:
        void homeShowToVisitors()
        {
            cout<< "show your sweet home"<<endl;
        }
};

class HomeDecorator : public HomeShow
{
    public:
        HomeDecorator(HomeShow *homeShow):m_homeShow(homeShow) {}
        
        void homeShowToVisitors()
        {
            m_homeShow->homeShowToVisitors();
        }
    public:
        HomeShow* m_homeShow;
};

class BirthdayHomeDecoration : public HomeDecorator
{
    public:
        BirthdayHomeDecoration(HomeShow *homeShow):HomeDecorator(homeShow){}
        
        void homeShowToVisitors()
        {
            cout<< "decorate home for birthday :) "<< endl;
            HomeDecorator::homeShowToVisitors();
        }
};

class CandleLightDinnerHomeDecoration : public HomeDecorator
{
    public:
        CandleLightDinnerHomeDecoration(HomeShow *homeShow):HomeDecorator(homeShow){}
        
        void homeShowToVisitors()
        {
            cout<< "decorate home for candle light dinner :) "<< endl;
            HomeDecorator::homeShowToVisitors();
        }
};
/*
#######################################################################################
#########  __  __       _        ######################################################
######### |  \/  | __ _(_)_ __   ######################################################
######### | |\/| |/ _` | | '_ \  ######################################################
######### | |  | | (_| | | | | | ######################################################
######### |_|  |_|\__,_|_|_| |_| ######################################################
#######################################################################################
*/
int main(int argc, char **argv)
{
    //normally showing home to friends and family.
    HomeShow *prtHomeShow = new HomeShowImple;
    prtHomeShow->homeShowToVisitors();
    
    // On someone's (special ones) birthday
    HomeShow *prtHomeShowForBdat = new BirthdayHomeDecoration(new HomeShowImple);
    prtHomeShowForBdat->homeShowToVisitors();
    
    // on candle light dinner with someone special for you.
    HomeShow *prtHomeShowForDinner = new CandleLightDinnerHomeDecoration(new HomeShowImple);
    prtHomeShowForDinner->homeShowToVisitors();
    return 0;
}

///////////////////////////////////////////////////////////////////-------------OutPut
/*
$ ./a.exe
show your sweet home
decorate home for birthday :)
show your sweet home
decorate home for candle light dinner :)
show your sweet home
*/

Thanks for reading it. To learn more about design patterns and basic design principles, please see my web page.

Comments

  1. Hi Kunjesh,

    Thank You for a nicely explained article on Decorator pattern, I have a small doubt about the usage of decorator, as visible from the class diagram, we have a concrete component class and an abstract decorator component class. My question is, is it mandatory to have a concrete component class? as the features could be a mix of any other feature, can we derive all our classes from Decorator component class?

    ReplyDelete
    Replies
    1. Decorator is decorating some object. Without having a base concrete implementation, whom it will decorate? The one you are suggesting is simple polymorphism (make everyone child of decorator). Then who will do pre and post processing of user request and decorate base object accordingly (as there will not be any base object, all become child of decorator, all become base object, even a decorator concrete implementation will become a base object).

      As per example mentioned in the blog, horizontal bar is a decorator concrete implementation, but this can't exist in isolation, it need a base notepad to decorate with horizontal bar, without notepad how horizontal bar can exist in isolation?

      Delete

Post a Comment

Popular posts from this blog

Non-virtual interface idiom (NVI)

Architectural patterns => Mud to structure => layers.

Architectural style -> Adoptable system -> Reflection.