Search code examples
c++design-patternsdecoratoranti-patterns

Decorator pattern vs. Call super anti-pattern


Let's have a simple Decorator example:

struct IStuff {
  virtual void Info()=0;
  virtual ~IStuff() { }
};

class Ugly : public IStuff {
public:
  void Info() { cout  << "Ugly"; }
};

class Shiny : public IStuff {
  IStuff* stuff;
public:
  Shiny(IStuff* stuff) {
    this->stuff = stuff;
  }
  ~Shiny() {
    delete stuff;
  }
  void Info() {
    stuff->Info(); // <------------------------------- call super?
    cout << "->Shiny";
  }
};

int main() {
  IStuff* s = new Ugly();
  s = new Shiny(s); // decorate
  s = new Shiny(s); // decorate more
  s->Info(); // Ugly->Shiny->Shiny
  delete s;
  return 0;
}

Is this also the Call super anti-pattern?

Call super is a design pattern in which a particular class stipulates that in a derived subclass, the user is required to override a method and call back the overridden function itself at a particular point.

Here is a little different implementation Is there any difference in design?


Solution

  • This is not Call super. You call the Info method of another IStuff instance, not the overriden version.

    Call super version:

    struct IStuff {
      // If you override this, you MUST call the base class version <-- call super
      virtual void Info()
      {
        // a default implementation.
    
        std::cout << "Super call ";  
      }
      virtual ~IStuff() { }
    };
    
    class Shiny : public IStuff {
    public:
      void Info() {
        IStuff::Info();  // don't forget to call base implementation.
        std::cout << "->Shiny";
      }
    };
    

    Some implementations of Decorator are making a super call to a Decorator base class, that is responsible to hold, call and manage the decorated reference:

    struct IStuff 
    {
      virtual void Info() = 0;
      virtual ~IStuff() { }
    };
    
    class Stuff : public IStuff
    {
    public:
        void Info() { std::cout << "Basic stuff"; }
    };
    
    class StuffDecorator : public IStuff
    {
        IStuff* decorated_;
    public:
        StuffDecorator(IStuff* decoratedStuff) :
            decorated_(decoratedStuff) {}
        ~StuffDecorator() { delete decorated_; }
    
        void Info()
        {
            decorated_->Info();
        }
    };
    
    class Shiny : public StuffDecorator 
    {
    public:
      Shiny(IStuff* stuff) : StuffDecorator(stuff) { }
    
      void Info() 
      {
        StuffDecorator::Info();
        std::cout << "->Shiny";
      }
    };
    

    To avoid the super call you might want to combine Decorator with Template Method:

    class StuffDecorator : public IStuff
    {
        IStuff* decorated_;
    public:
        StuffDecorator(IStuff* decoratedStuff) :
            decorated_(decoratedStuff) {}
        ~StuffDecorator() { delete decorated_; }
    
        void Info()
        {
            decorated_->Info();
            DoInfo();
        }
    private:
        // Template method
        virtual void DoInfo() = 0;
    };
    
    
    class Shiny : public StuffDecorator 
    {
    public:
      Shiny(IStuff* stuff) : StuffDecorator(stuff) { }
    private:
      void DoInfo() 
      {
        std::cout << "->Shiny";
      }
    };