7

What should be at the top of inheritance tree of Decorator design pattern? I mean the base for both components and decorators

  • non-abstract class: has data fields, implements methods
  • abstract class: has data fields, has only abstract (pure virtual) methods
  • interface: no data fields, has only abstract (pure virtual) methods
  • abstract class & interface: interface is the base for decorator, abstract class is the base for decorated components

Simple example

  • IText - interface declares getContent() method
  • AbstractText - abstract class declares getContent() method and defines txt data field
  • Text - non-abstract class, general, can be also used as base for other classes
  • HelloWorld - non-abstract class, more specific than Text
  • HelloSomeone - non-abstract class, extends Text with the data field name
  • Decorator abstract class for decorators (might be redundant?)
  • PrefixDecorator decorator class prefixes getContent() with a string prefix
  • SuffixDecorator decorator class suffixes getContent() with a string suffix
  • getContent() method that should return txt with optional additional data (suffixes and prefixes from decorators, name field in the HelloSomeone class)

What base class should be chosen?

  • non-abstract class enter image description here

  • abstract class enter image description here

  • interface enter image description here

  • abstract class & interface enter image description here

Code for last example

#include <iostream> 
#include <string> 
using namespace std;

/* Base for decorator & abstract: no fields, getContent()  is pure virtual */
class IText {
public:
    virtual string getContent() = 0;
    virtual ~IText() { }
};

/* Base: has a field, getContent()  is pure virtual */
class AbstractText : public IText {
protected: 
    string txt;
public:
    virtual ~AbstractText() { cout << " AbstractText dtor" << endl; }
    AbstractText(string t = "...") : txt(t) { }
    virtual string getContent() = 0;
};

/* Concrete, general */
class Text : public AbstractText {
public:
    virtual ~Text() { cout << " Text dtor" << endl; }
    Text(string t) : AbstractText(t) { }
    virtual string getContent() { return txt; }
};

/* Concrete, more specific */
class HelloWorld : public AbstractText {
public:
    virtual ~HelloWorld() { cout << " HelloWorld dtor" << endl; }
    HelloWorld() : AbstractText("Hello world") {}
    virtual string getContent() { return txt; }
};

/* Additional field */
class HelloSomeone : public AbstractText {
private:
    string name;
public:
    virtual ~HelloSomeone() { cout << " HelloSomeone dtor" << endl; }
    HelloSomeone(string n) : AbstractText("Hello, "), name(n) { }
    string getContent() { return (txt + name + "!"); }
};


class Decorator : public IText {
public:
    virtual string getContent() = 0;
    virtual ~Decorator() { cout << " Decorator dtor" << endl; }
};

class PrefixDecorator : public Decorator {
private:
    IText *t;
public:
    PrefixDecorator(IText *te)  { t = te; }
    virtual ~PrefixDecorator() { cout << " PrefixDecorator dtor" << endl; delete t; }
    virtual string getContent() { return "---" + t->getContent(); }
};

class SuffixDecorator : public Decorator {
private:
    IText *t;
public:
    SuffixDecorator(IText *te)  { t = te; }
    virtual ~SuffixDecorator() { cout << " SuffixDecorator dtor" << endl; delete t; }
    virtual string getContent() { return t->getContent() + "---"; }
};

int main() {
    IText *t = new Text("some text");
    IText *hw = new HelloWorld();
    IText *hs = new HelloSomeone("Mark");
    cout << t->getContent() << endl;
    cout << hw->getContent() << endl;
    cout << hs->getContent() << endl;
    t = new PrefixDecorator(t);
    hw = new PrefixDecorator(hw);
    hw = new SuffixDecorator(hw);
    cout << t->getContent() << endl;
    cout << hw->getContent() << endl;
    cout << "DELETE Text" << endl;
    delete t;
    cout << "DELETE Helloworld" << endl;
    delete hw;
    cout << "DELETE HelloSomeone" << endl;
    delete hs;
}
2
  • Also there is no technical difference between an interface and an abstract class in c++. I would go with the abstract class because it's the more common approach.
    – AK_
    Apr 1, 2015 at 12:48
  • @AK_ and C++ "interface" must have destructor Apr 3, 2015 at 17:35

2 Answers 2

5

What should be at the top of inheritance tree of Decorator design pattern?

How to discriminate:

  • non-abstract class - Only if it makes sense in your code to instantiate it in client code (also see: liskov substitution)
  • abstract class or interface - most common case; This is when it doesn't make sense for client code to instantiate it; To distinguish between abstract class and interface: If you look through your code and find that all specializations have data in common, move it to the base class (otherwise, remain with the interface).
  • abstract class and interface - if there are two cases when a part of your specializations have common code and a part don't, extract common code to separate class, and you end up with both cases.

My criteria is usually not derived from some pure rules that I respect to implement the decorator; Instead, I try to optimize for maintenance instead of purity.

You can also see that I do not particularly differentiate between abstract classes and interfaces; this is because in C++ there are no interfaces - only abstract classes (so the distinction feels a bit artificial).

1

It depends entirely on what you want. These variations exist for your convenience, not because one way is a better way than another.

Personally I'm partial to the non-abstract class, because it requires the fewest number of classes, which in my opinion is always a plus. However, you may find yourself in the situation in which there is an AbstractText class or IText and an Text implementation, in which case you may model your Decorator pattern accordingly.

If you do decide to choose the non-abstract class variation, it is probably the easiest to adapt into the other variations later since it is usually easier to add classes than to remove them, so I would give the non-abstract class variation a try.

1
  • I wanted to know if all solutions are valid. I'm thinking about something. When decorator inherits from class that has data fields it also inherits that data fields. However decirator does not use its own data fields. It asks the referenced components for its data fields. Apr 3, 2015 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.