Design Pattern

Decorator Design Pattern

“linked list of features”

The decorator design pattern allows us to add new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators.

When to use Decorator Pattern

Add / remove behavior at run-time with a polymorphic class.

Resources

Formally learned in CS247.

Pieces of the decorator pattern:

  • Abstract Component: Gives the interface for our classes
  • Concrete Component: Implements the “basic” version of the interface
  • Abstract Decorator: Organizes decorators and has-a decorator or the concrete component.
  • Concrete Decorators: Implement the interface, call operation() on the next object in this linked list.

Template code for a Pizza example (UML below)

class Pizza {
	public:
		virtual ~Pizza(){}
		virtual float price() const = 0;
		virtual string desc() const = 0;
};
 
class CrustAndSauce: public Pizza {
	public:
		float price() const override {return 5.99;};
		string desc() const override {return "pizza";};
};
 
class Decorator: public Pizza {
	protected:
		Pizza* next;
	public:
		Decorator(Pizza* next): next{next} {}
		~Decorator() {delete next;}
};
 
class Topping: public Decorator {
	string name;
	public:
		Topping(const string& s, Pizza* p): Decorator{p}, name{s} {}
		float price() const override {
			return 0.75 + next->price();
		}
		string desc() const override {
			return next->desc() + " with" + name;
		}
};
 
class StuffedCrust: public Decorator {
	public:
		StuffedCrust(Pizza* p): Decorator{p} {}
		float price() const override {
			return next->price() + 2.50;
		}
		string desc() const override {
			return next->desc() + " with stuffed crust";
		}
};
 
Pizza* p = new CrustAndSauce{};
p = new Topping{"cheese", p};
p = new Topping{"pepperoni", p};
p = new StuffedCrust{p};
cout << p->price() << " " << p->desc() << endl;
// Output: 9.99 pizza withcheese withpepperoni with stuffed crust
delete p;

StuffedCrust Topping Topping CrustAndSauce

Motivation

Motivation for Decorator

Consider the following design scenario, where we want to enable customization for a windowing (GUI) system.

If we attempt to make a class for each possible feature, for features, there are possible configurations. Combinatorial explosion!

The solution is to use a decorator design pattern!

Window* w = new ScrollBar{new Tabbing{new BasicWindow{}}};

ScrollBar Tabbing Basic Window

w->render()