Click here to Skip to main content
15,867,298 members
Articles / Programming Languages / C#

"Please Don’t Fail Me!" - Decorator Cries Out to Template Method

,
Rate me:
Please Sign up or sign in to vote.
4.68/5 (13 votes)
13 Sep 2009CPOL6 min read 54.8K   145   20   22
Are your classes that implement the Template Method Design Pattern "Decorator aware"?

Introduction

This article is a result of the serendipity experienced when implementing a small framework. The framework had a bunch of classes, which implemented some of the widely used design patterns. One of the classes implemented the Template Method pattern. The situation then was to make that class extensible without modifying it. The Decorator pattern seemed the right fit to extend (decorate) the class.

Besides providing a level of sophistication in code and the fun in its use, design patterns do not reveal until asked for the problems in mixing them. This article explains the problems when mixing Decorator and Template Method patterns. The article also discusses the possible solutions to circumvent the problem in mixing the above design patterns. Know why these patterns don't gel well? Please read on.

Assumptions

This article assumes knowledge in the following areas:

  1. Object Oriented Programming
  2. Design Patterns, especially Decorator and Template Method

Problem

Consider a Shape class like the one below, which is the base class for all Shapes, viz, Circle, Rectangle, Square, etc.

C++
class Shape
{
private: std::string _ type;

public: Shape(const std::string& type) : _ type (type)
        {
        }

public: std::string Type() const
        {
            return _type;
        }

protected: virtual void CreateDC() = 0;
protected: virtual void InitDC() = 0;
protected: virtual void Paint() = 0;
protected: virtual void ReleaseDC() = 0;

public: void Draw()
        {
            cout << "Drawing " << Type();

            CreateDC();
            InitDC();
            Paint();
            ReleaseDC();
        }
};

The Shape class listed above implements the template method design pattern via the Draw method. The custom shapes like Circle or Rectangle would have to implement the pure virtual functions – CreateDC, InitDC, Paint, ReleaseDC - in order to be drawn.

For instance, a Circle shape may be hypothetically implemented as follows:

C++
class Circle : public Shape
{
public: Circle() : Shape("Circle")
        {
        }

protected: void CreateDC()
           {
               cout << std::endl << "Circle::CreateDC";
           }

protected: void InitDC()
           {
               cout << std::endl << "Circle::InitDC";
           }

protected: void ReleaseDC()
           {
               cout << std::endl << "Circle::ReleaseDC";
           }

protected: void Paint()
           {
               cout << std::endl << "Circle::Paint";
           }
};

The typical usage of the above classes would be as follows:

C++
void main()
{
    //
    // Previous Code...
    //

    Shape* s = new Circle();
    s->Draw();

    //
    // Later Code...
    //
}

The output of the above program should be obvious.

As in the story of any application development, the Circle must be extensible without modifying the existing classes. For instance, the Circle may be filled; its border color and thickness may be changed, and so on. Let us say we want to fill the Circle. We have a couple of options:

  1. Derive a class from Circle, say FilledCircle and override the Paint method - This technique is not scalable, and cannot be used for filling other types of shapes. Besides, this is an approach which would result in a combinatorial explosion of classes depending on the extenders desired.
  2. Use a decorator, say ShapeFiller - This technique is extremely scalable since we can apply this to any Shape class.

Per the topic of the discussion, we choose Option #2.

Following is a hypothetical implementation of the ShapeFiller (Decorator) class:

C++
class ShapeFiller : public Shape
{
private: Shape* _shape;

public: ShapeFiller(Shape* shapeTobeFilled) : Shape(shapeTobeFilled->Name()),
           _shape(shapeTobeFilled)
        {
        }

protected: void CreateDC()
           {
              // CDC1: Call the underlying shape's CreateDC
              _shape->CreateDC();

              cout << std::endl << "ShapeFiller::CreateDC";
           }

protected: void InitDC()
           {
              // IDC1: Call the underlying shape's InitDC
              _shape->InitDC();

              cout << std::endl << "ShapeFiller::InitDC";
           }

protected: void ReleaseDC()
           {
              // RDC1: Call the underlying shape's ReleaseDC
              _shape->ReleaseDC();

              cout << std::endl << "ShapeFiller::ReleaseDC";
           }

protected: void Paint()
           {
              // Paint1: First call the underlying shape's Paint
              _shape->Paint();

              // Paint2: Custom logic to fill the shape (forget the color).
              // Do you see it filling?

              cout << std::endl << "ShapeFiller::Paint";
           }
};

There are several things to be observed in the above implementation:

  1. The above class will not compile, complaining the following error:
    error C2248: 'Shape::CreateDC' : 
    		cannot access protected member declared in class 'Shape'
    error C2248: 'Shape::InitDC' : 
    		cannot access protected member declared in class 'Shape'
    error C2248: 'Shape::ReleaseDC' : 
    		cannot access protected member declared in class 'Shape'
    error C2248: 'Shape::Paint' : 
    		cannot access protected member declared in class 'Shape'
  2. Although no custom logic is required in the CreateDC, InitDC and ReleaseDC methods of the ShapeFiller, they must have to be implemented simply as place holders forwarding calls to the underlying shape (needless those methods are pure virtual). The quickest remedy may seem to have default (empty) implementation for the above methods in the Shape class. Doing so would not be creating (init and release) the DC when using the ShapeFiller class. In other words, application will misbehave (probably crash) when attempting to Paint().
  3. The other (ugly) remedy may be making the protected methods - CreateDC, InitDC, ReleaseDC - public. It is a bad design choice. In the Object Oriented world, it is a crime.

So that is the problem - Attempting to extend a class that implements Template Method design pattern using Decorator design pattern results in a near-predicament situation.

Intent of Decorator Pattern [GoF]

  • Attach additional responsibilities to an object dynamically.
  • Decorators provide a flexible alternative to sub-classing for extending functionality.

Intent of Template Method Pattern [GoF]

  1. Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
  2. Template Method lets subclasses redefine certain steps of an algorithm without letting them to change the algorithm's structure.

As stated in the intent, the Decorator pattern avoids sub-classing while the Template Method pattern relies on it. Secondly, the Template Method need not publicize all the steps involved in an algorithm. On the other hand, the Decorator pattern relies on the public interface of the class it intends to decorate. Evidently, in our case, adopting the decorator pattern blows the purpose of the Template Method pattern.

Conclusion – Is there a Solution?

Yes and No.

YES

Approach 1: Make the class that uses the template method design pattern Decorator Pattern aware.

Consider the new Shape class:

C++
class Shape
{
friend class ShapeDecoratorBase; // <== Added friend declaration

private: std::string _type;

public: Shape(const std::string& type) : _type (type)
        {
        }

public: std::string Type() const
        {
           return _type;
        }

protected: virtual void CreateDC() = 0;
protected: virtual void InitDC() = 0;
protected: virtual void Paint() = 0;
protected: virtual void ReleaseDC() = 0;

public: void Draw()
        {
	       cout << "Drawing " << Type();

           CreateDC();
           InitDC();
           Paint();
           ReleaseDC();
        }
};

Instead of every individual decorator, bloat the code with redundant code that forwards calls to the underlying shape object, a common ShapeDecoratorBase base class is introduced. In other words, ShapeDecoratorBase truly implements the decorator pattern. This class is made a friend of the Shape class (Yes, a friend. Some readers might think that using friends is a bad design. But not really, follow the link "When to use friends"). The individual decorators may now derive from ShapeDecoratorBase and implement the Paint method to decorate the underlying shape.

The ShapeDecoratorBase class is defined as follows:

C++
class ShapeDecoratorBase : public Shape
{
protected: Shape* _shape;

public: ShapeDecoratorBase (Shape* shapeTobeFilled) : Shape(shapeTobeFilled->Name()),
           _shape(shapeTobeFilled)
        {
           cout << std::endl << "ShapeDecoratorBase Ctor";
        }

public: ~ShapeDecoratorBase()
        {
           cout << std::endl << "ShapeDecoratorBase Dtor";
        }

protected: void CreateDC()
           {
              cout << std::endl << "ShapeDecoratorBase::CreateDC";

              // CDC1: Call the underlying shape's CreateDC
              _shape->CreateDC();
           }

protected: void InitDC()
           {
              cout << std::endl << "ShapeDecoratorBase::InitDC";

              // IDC1: Call the underlying shape's InitDC
              _shape->InitDC();
           }

protected: void ReleaseDC()
           {
              cout << std::endl << "ShapeDecoratorBase::ReleaseDC";

              // RDC1: Call the underlying shape's ReleaseDC
              _shape->ReleaseDC();
           }

protected: void Paint()
           {
              cout << std::endl << "ShapeDecoratorBase::Paint";

              // Paint1: First call the underlying shape's Paint
              _shape->Paint();
           }
};

Here is the revised implementation of the ShapeFiller Decorator class:

C++
class ShapeFiller : public ShapeDecoratorBase
{
public: ShapeFiller(Shape* shapeTobeFilled) : ShapeDecoratorBase(shapeTobeFilled)
        {
           cout << std::endl << "ShapeFiller Ctor";
        }

public: ~ShapeFiller()
        {
           cout << std::endl << "ShapeFiller Dtor";
        }

protected: void Paint()
           {
              ShapeDecoratorBase::Paint();

              // Paint2: Custom logic to fill the shape (forget the color).
              // Do you see it filling?

              cout << std::endl << "ShapeFiller::Paint";
           }
};

The ShapeFiller class now works as expected. We can now have more classes like ShapeBorderThickener, etc. implemented similarly.

Approach 2: Using member function pointers to implement the template method design pattern. This technique is not discussed further since it is a far less elegant approach than Approach 1. In other words, it hurts the eyes of an object oriented programmer. However, the source code is attached for reference.

Downside

The major downside of these two approaches is that both require access to the source code of the base class (The Shape class in our case). This might not be the case in all situations.

If the framework implementer had not given forethought about this problem, there is no elegant solution.

Needless to say, the ShapeDecoratorBase class has to be provided by the framework designer and not by the user of the framework.

It must be understood that it is not just the Template Method class that should be made aware of the Decorator but it is the Programmer/Framework designer who must be aware of such scenarios. It is the framework designer who decides if a class should be made extensible or not.

NO

The situation cannot be resolved if the source of the base class is not accessible. If the template method class is NOT implemented to be Decorator aware, the Decorator Pattern cannot be used.

The other option, which is inheritance, has to be pursued that has its own set of problems.

The Final Word

We hope this article has brought to light a subtle and interesting issue when using the Decorator design pattern with the Template Method design pattern.

"When you implement a framework, give a thought if your classes that implement the Template method design pattern need to be made Decorator aware."

Happy designing!

History

  • 4-Sep-2009: Initial draft
  • 8-Sep-2009: Corrected typos per comment from UGoetzke
  • 9-Sep-2009: Added C# code sample
  • 13-Sep-2009: Rephrased the introduction

License

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


Written By
Architect
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Written By
Technical Lead HCL Technologies LTD
India India
I am Sanjeev Venkat working for HCL Technologies since 2003. I've 9 years of experience in Software Development. My core expertise include Windows Device Drivers, COM, C++, .NET.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Member 270799017-Dec-09 4:26
Member 270799017-Dec-09 4:26 
GeneralRe: My vote of 1 Pin
Sanjeev Venkat24-Feb-10 7:50
Sanjeev Venkat24-Feb-10 7:50 
GeneralMissing point Pin
Ashish Sheth14-Sep-09 2:24
Ashish Sheth14-Sep-09 2:24 
GeneralRe: Missing point Pin
kb-boxer15-Sep-09 23:59
kb-boxer15-Sep-09 23:59 
QuestionShapeFiller inherits from Shape? Pin
Andreone9-Sep-09 13:45
Andreone9-Sep-09 13:45 
AnswerRe: ShapeFiller inherits from Shape? Pin
Sanjeev Venkat9-Sep-09 18:23
Sanjeev Venkat9-Sep-09 18:23 
GeneralRe: ShapeFiller inherits from Shape? [modified] Pin
Andreone10-Sep-09 0:18
Andreone10-Sep-09 0:18 
GeneralRe: ShapeFiller inherits from Shape? Pin
Sanjeev Venkat10-Sep-09 1:14
Sanjeev Venkat10-Sep-09 1:14 
GeneralRe: ShapeFiller inherits from Shape? Pin
Andreone10-Sep-09 1:30
Andreone10-Sep-09 1:30 
GeneralRe: ShapeFiller inherits from Shape? [modified] Pin
Sanjeev Venkat10-Sep-09 2:14
Sanjeev Venkat10-Sep-09 2:14 
GeneralRe: ShapeFiller inherits from Shape? Pin
Andreone10-Sep-09 7:06
Andreone10-Sep-09 7:06 
GeneralRe: ShapeFiller inherits from Shape? [modified] Pin
Sanjeev Venkat10-Sep-09 8:09
Sanjeev Venkat10-Sep-09 8:09 
GeneralRe: ShapeFiller inherits from Shape? Pin
kb-boxer10-Sep-09 21:19
kb-boxer10-Sep-09 21:19 
GeneralRe: ShapeFiller inherits from Shape? Pin
Qwertie27-Sep-09 12:43
Qwertie27-Sep-09 12:43 
GeneralRe: ShapeFiller inherits from Shape? Pin
kb-boxer29-Sep-09 18:02
kb-boxer29-Sep-09 18:02 
GeneralMy vote of 1 Pin
Tomas Rapkauskas8-Sep-09 4:46
Tomas Rapkauskas8-Sep-09 4:46 
GeneralRe: My vote of 1 [modified] Pin
Sanjeev Venkat8-Sep-09 20:27
Sanjeev Venkat8-Sep-09 20:27 
GeneralRe: My vote of 1 (about friend class usage) Pin
Tomas Rapkauskas8-Sep-09 23:45
Tomas Rapkauskas8-Sep-09 23:45 
GeneralRe: My vote of 1 (about friend class usage) Pin
kb-boxer9-Sep-09 20:32
kb-boxer9-Sep-09 20:32 
(Comments inline...)

About friend class usage in the base class I still do not agree:
I agree with yours design - this is more or less correct if we using C++ language.
I also do such trick if I am using third party library and I do not make any "noise" in the base class.
e.g.
class CFriendShape:public Shape{friend class ShapeDecoratorBase;}
class ShapeDecoratorBase : public Shape {
public: ShapeDecoratorBase (Shape* shape) {
m_shape = (CFriendShape *)shape
private: CFriendShape *m_shape;};
this is also not nice, but sometimes helps to implement decorator pattern design,
if 3'rd part framework does not think about possible decorations.
Hypothetical question: Should it? Absolutely Yes. After vehement comments, making an assumption that all Shape objects are CFriendShape (the type cast above) is not wise. friend is a valid and legal C++ where as the above type cast is a trick. There is every chance that in an implementation of C++ the above cast may fail. In that sense, the solution proposed adds no noise to the library.

friend usage with current context it is not a nice solution,
because you are talking about OO design patterns and as I understand
patterns can be used with any OO programing language,
so current solution is very specific for the C++ language,
if you still think, that current design is OO design,
Could you provide an equivalent example with other OO (e.g.: Eiffel) languages? No OO programing language rigidly implements OO principles. Even more, every OO PL deliberately exposes primitives (such as friend in C++) to circumvent the real time design problems with pure object orientation. Eiffel might be a pure OO PL but definitely far less used and time-tested when compared to C++\C#. friend is not an enemy. It is a tool in C++ for class colloboration during development and not during deployment. The source download includes a C# implementation of the solution (using protected internal).

So finally:
A. You design breaks OO design principles:
A.1.Liskov substitution:
http://en.wikipedia.org/wiki/Liskov_substitution_principle
A.2. understandability, Comprehensibility - it's hard to understand friend
class declaration in the base class, it is a noise in the code, with out any
good explanation.

B. problems with decorator pattern
(checkout: http://www.research.ibm.com/sop)
B.1. Indirection/ confusion
B.2. Object Schizophrenia
B.3. Preplanning

B.1. Indirection/ confusion:
It can cause confusion to the maintainers of the code,
because they have to understand that such calls implement the logics
of the same object and therefore have to be distinguished
from the calls implementing interaction with other object.

B.2. Object Schizophrenia
checkout Broken Delegation, Broken Assumptions and “Doppelgangers”

B.3.
Yours solution must be used in advance of the actual need to exploit their flexibility.
This requires extra programming effort and results in extra complexity and inefficiency of the software.


Anyway as I mention problem exist not in the decorator pattern usage, but in the yours example base class design.The problem is simple - the article is not about pros and cons of decorator pattern. And the example class chosen is any Foo and Bar example; or the boring Widget example in the Gof book. Reader may imagine any big or small class he likes or might have come across for the situation. There are inherent problems (or roadblocks) in mixing patterns. One such is decorator with template method. This article discusses about it. Now arguing why would one not use a pattern other than decorator for decoration is not in the scope of this article. Other better solutions for the problem are welcome.

Thanks
Vivek Ragunathan


http://vivekragunathan.spaces.live.com
Programming is an art. Code is a poem

GeneralEnlighting Example Pin
UGoetzke8-Sep-09 2:53
UGoetzke8-Sep-09 2:53 
GeneralRe: Enlighting Example Pin
Sanjeev Venkat8-Sep-09 2:56
Sanjeev Venkat8-Sep-09 2:56 

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.