Back to Design Patterns Study Group page

An Example of the Decorator Pattern (p 175)

Intent

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extended functionality.

In this example we are decorating patients with problems. We decorate a patient called "Clyde" who is hung over, smashed his knee, and writes buggy code. "Bonnie" is a sane patient who is also hung over.

NOTE: Please see below for a commentary on this example by Design Patterns author Ralph Johnson.

How "Clyde" Explains his Problems

A depiction of the Decorated Patient Object

Illness decorations on object "Clyde" and the recusive calls to his ExplainProblem() method.

Example Program illustrating a Decorator Pattern for Patients

Structure

Code

#include <iostream.h>
/*
  Example of the Decorator Pattern (p. 175)
  J. Kerievsky, October 1995
*/

class Person
{
  public:
    virtual void Explain_Problems() {}
  protected:
    Person() { }
};


class Patient : public Person
{
 public:
   Patient() : Person() {}
   void Explain_Problems() {}
};

class PatientDecorator : public Person
{
 public:
   PatientDecorator(Person* p) : Person() { _NextPatientDecorator = p; }
   void Explain_Problems() { _NextPatientDecorator->Explain_Problems(); }
 private:
   Person* _NextPatientDecorator;
};


class SanePatient : public PatientDecorator
{
 public:
   SanePatient(Person* p) : PatientDecorator(p) { }
   void Explain_Problems() {
     cout << "\tSane." << endl;
     PatientDecorator::Explain_Problems(); }
};


class HungOverPatient : public PatientDecorator
{
 public:
   HungOverPatient(Person* p, int num_drinks = 5) : PatientDecorator(p)
      { _drinks_drunk = num_drinks; }
   void Explain_Problems() {
      Explain();
      PatientDecorator::Explain_Problems(); }
 protected:
   void Explain() { cout << "\tHungover";  Burp();  }
   void Burp() { if (_drinks_drunk > 5) cout << " (Buuuuurrppp)";  cout << "." << endl; }
 private:
   int _drinks_drunk;
};


class BugRiddenPatient : public PatientDecorator
{
 public:
   BugRiddenPatient(Person* p) : PatientDecorator(p) {}
   void Explain_Problems() {
      cout << "\tA buggy software developer." << endl;
      PatientDecorator::Explain_Problems(); }
};


class SmashedKneePatient : public PatientDecorator
{
 public:
   SmashedKneePatient(Person* p) : PatientDecorator(p) {}
   void Explain_Problems() {
      cout << "\tNursing a smashed knee." << endl;
      PatientDecorator::Explain_Problems(); }
};


void TellMeWhatIsWrong(Person* p, char* n);

void main(void)
{
  // mix and match illness decorations for a patient
  Person* Clyde = new HungOverPatient(
                      new SmashedKneePatient(
                        new BugRiddenPatient(
                          new Patient())),6);
  TellMeWhatIsWrong(Clyde, "Clyde");


  Person* Bonnie = new SanePatient(
                      new HungOverPatient(
                        new Patient()));
  TellMeWhatIsWrong(Bonnie, "Bonnie");
}


void TellMeWhatIsWrong(Person* p, char* n)
{
   cout << n << " is " << endl;
   p->Explain_Problems();
   cout << endl;
}


Output of Example Program

Clyde is Hungover (Buuuuurrppp). Nursing a smashed knee. A buggy software developer. Bonnie is Sane. Hungover.

Commentary on the Patient Decorator

Ralph Johnson writes Note that Decorator can often be transformed into Strategy, and it can here. Suppose that the Patient class had an instance variable that was a Problem. You would make a subclass of Problem for each particular kind of problem, like "hung-over", "bad knee", etc. You could use the Composite pattern and make a CompositeProblem for those people who have more than one problem! You would also need a NoProblem subclass of Problem for those who are perfect. This is the "Null Object" pattern that Bruce Anderson described a year or two ago on the patterns list.