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,
- Invasive, where we change the internal system to implement new behavior.
- 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 */
Hi Kunjesh,
ReplyDeleteThank 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?
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).
DeleteAs 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?