C++ – What kind of base for Decorator: interface, abstract class, non-abstract

cdecoratordesigndesign-patterns

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;
}

Best Answer

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).

Related Topic