Amazon.com Widgets Delphi and the Decorator Pattern

Delphi and the Decorator Pattern

By Nick at February 04, 2013 00:23
Filed Under: Delphi, Patterns

I’m currently reading Head First Design Patterns, and am finding it very useful and educational.  One problem, though – it’s all in Java.  So I thought that as part of the exercises, I’d translate the code to Delphi.  And also as part of my learning process, I thought it would be a good idea to post an article about each of the patterns.  I also encourage you to buy the book and read it for yourself.

Let me be clear – I’m not doing any more than reproducing the demos in the book.  I’m very aware that there are better ways to implement the patterns than are shown here.  The idea is to learn the pattern, not optimize the code.

Continuing on through HFDP, the next pattern we’ll cover is the Decorator Pattern. 

One of the themes of the book is to make your code as flexible as possible at runtime, and to design your code to prefer runtime flexibility over compile time flexibility.  Anytime you determine something at compile-time, you lock that feature in to your code.  It can’t be changed at runtime. 

Inheritance is a pillar of OOP, but it is a compile-time construct.  Inheritance is powerful, but inflexible at runtime.  You have the classes you have, and they can’t be altered or changed once you set them in the stone that is compiled code. If you are trying to come up with differing combinations to decorate each class, you can end up having a huge number of descendent classes which can become difficult to manage and maintain.  Changes to one of the features means any number of descendants might need to change as well. 

The decorator pattern is a way of circumventing these limitations, and providing the power of inheritance while at the same time providing run-time flexibility.  It actually uses some simple inheritance to “wrap up” a given class and provide additional functionality without having to change the original class. 

Here is the formal definition from Wikipedia:  The Decorator Pattern attaches additional responsibilities to an object dynamically.  Decorators provide a flexible alternative to sub-classing for extending functionality.  You can read more about the pattern on Wikipedia.

It’s actually pretty straight-forward.  Here’s what you do:

  • First, you start with the notion of a class that needs to be decorated.  It becomes the “base” class.  The example in HFDP is that of a coffee drink.  They coffee itself – a beverage – is the base class which can be any number of different coffee types. Each will be decorated with various flavorings, etc., such as milk, mocha, and cream whip. 
  • Then you create a single descendant that will be the base decorator class.  That class will take as a property an instance of the original base class.  The base decorator should have a constructor that takes the original base class as a parameter. 
  • Next, you create concrete instances of decorators that override the necessary methods and properties of the base decorator class.
  • From there, you can start with the base class, and then chain together as many decorators as you want.

Okay, let’s take a look at that in code.  Here is the base class, TBeverage:

type
  TBeverage = class
  private
    FDescription: string;
  protected
    function GetDescription: string;  virtual;
    procedure SetDescription(const Value: string);
  public
    function Cost: Double; virtual; abstract;
    property Description: string read GetDescription write SetDescription;
  end;

The base class is abstract.  It has two properties, with the Cost property being read-only and having an abstract, virtual getter. 

TBeverage is the base class for the beginning of the wrapping process.  We create descendants that will form the base class described above.  These classes override the Cost method, providing their own prices.  They will be the starting point for decorating.  In our case, we’ll create specific base coffee types that can be wrapped:

 TEspresso = class(TBeverage)
    constructor Create;
    function Cost: Double; override;
  end;

  THouseBlend = class(TBeverage)
    constructor Create;
    function Cost: Double; override;
  end;

  TDarkRoast = class(TBeverage)
    constructor Create;
    function Cost: Double; override;
  end;

  TDecaf = class(TBeverage)
    constructor Create;
    function Cost: Double; override;
  end;

The interesting part comes in the base decorator class:

  TBeverageDecorator = class(TBeverage)
  private
    FBeverage: TBeverage;
  public
    constructor Create(aBeverage: TBeverage);
    destructor Destroy; override;
  end;

TBeverageDecorator does three things.  One, it descends from TBeverage. Two, it gets another instance of TBeverage from its constructor in order to “wrap” itself around the starting base class.  And three, the destructor ensures that the internally stored instance gets freed properly when that time comes. 

Since TBeverageDecorator is itself a TBeverage, you can continue wrapping the previous result in a new one, decorating the decorator as it were.  In addition, it will have the exact same interface as the base beverage class, so it can act like a beverage because, well, it is one.   

As for classes that do the decorating, they will descend from TBeverageDecorator:

  TMocha = class(TBeverageDecorator)
  protected
    function GetDescription: string; override;
  public
    function Cost: Double; override;
  end;

  TSoy = class(TBeverageDecorator)
  protected
    function GetDescription: string; override;
  public
    function Cost: Double; override;
  end;

  TWhip = class(TBeverageDecorator)
  protected
    function GetDescription: string; override;
  public
    function Cost: Double; override;
  end;

Each of these classes does two things. 

First, they override the getter for the Description property.  They do it in an interesting way.  Since they are decorating the base Beverage, they assume that they are “additive”, that is that they will be adding on to the base description.  The TMocha.GetDescription method looks like this:

function TMocha.GetDescription: string;
begin
  Result := FBeverage.GetDescription + ', Mocha';
end;

This method grabs the description from the class that it is wrapping and adds on a comma and its own description.

Second, the Cost method is overridden to add the cost to the price of the class that it is decorating.

function TMocha.Cost: Double;
begin
  Result := 0.20 + FBeverage.Cost;
end;

So, now we have a bunch of types of coffee – the base classes --and a bunch of flavors to wrap around those classes and themselves.  Let’s put it all together:

First, we’ll create a routine to write out the coffee type to the console:

procedure OutputBeverage(aBeverage: TBeverage);
begin
  WriteLn('A ', aBeverage.Description, ' costs ', '$', Format('%2f', [aBeverage.Cost]));
  WriteLn;
end;

Then, we’ll create an Espresso and wrap it with Double Mocha and Whip:

procedure OrderCoffee;
var
  Beverage: TBeverage;
begin
  Beverage := TEspresso.Create;
  try
    Beverage := TMocha.Create(Beverage);
    Beverage := TMocha.Create(Beverage);
    Beverage := TWhip.Create(Beverage);
    OutputBeverage(Beverage);
  finally
    Beverage.Free;
  end;
end;

This will build the description and calculate the correct price, and result in:

 

 

 

Another way that you can create a coffee is by simply nesting each decorator in the constructor of the previous one.  This method actually provides a coding representation of how each decorator wraps up the previous one:

  // Alternate way to call the same thing as above....
  Beverage3 := TWhip.Create(TMocha.Create(TMocha.Create(TDarkRoast.Create)));
  try
    OutputBeverage(Beverage3);
  finally
    Beverage3.Free;
  end;

The complete implementation of our little coffee shop application can be found on BitBucket.  That code should be a faithful representation of the final project in the book.  And I have mentioned that I strongly recommend that you buy the book, right?

So, to summarize:

  • The whole thing starts with base class that will be decorated.
  • The decorator classes both descend from and maintains a reference to that base class. This means that they will have the same interface, yet the reference will allow composition instead of inheritance. 
  • One or more decorators can be wrapped around the base class and each other to produce a single entity. 
  • The Decorator can augment the behavior of the class it is wrapping.  We see this with the Cost and Description properties. 
  • You can dynamically decorate objects at anytime, enabling you to create any combination of decorators without having to create a descendent class for each.
  • Decorators allow you to extend behavior of existing classes without needing to modify existing code. 

There are some problems here – you can end up with a lot of little classes to manage; that might bother some people.  Use of decorators can lead to complex code and implementations – use them with care. 

However, the basic idea is to simplify and add flexibility to your code over a complex inheritance model. In addition, it makes it easier to build a single instance at run-time of complicated combinations of features or items.

Next up:  The Factory Pattern

blog comments powered by Disqus

My Book

A Pithy Quote for You

"Christianity, if false, is of no importance, and if true, of infinite importance. The only thing it cannot be is moderately important."    –  C. S. Lewis

Amazon Gift Cards

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.