Decorator Design Pattern in Delphi. Single decoration

Decorator (also referred as Wrapper) is classified by GoF as a structural pattern. Its purpose is to:

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

Both inheritance and the decorator pattern aim towards extending the functionality. This is what they have in common.

There are a couple of remarkable differences:
  • Inheritance extends the functionality at compilation time (statically). The decorator pattern extends the functionality at runtime (dynamically).
  • Inheritance extends the functionality of a whole class (all objects of the extended class get the extended functionality). The decorator pattern allows extending the functionality of a selected object (or group of objects) without affecting the remaining objects.  

You can think of the decorator pattern as a way to add make-up to an object or even as a way to attach accessories to that object. All this is done on the fly after the object itself has been created.

Let’s walk through a simple task to get the idea. This example might sound silly. I want it silly so that you can focus on the decorator implementation, avoiding any extra complexity.

Please, be aware that this design is somewhat unfinished, since we are only covering the case for a single decoration (just one functionality to be extended). In real life, we will need multiple decorators in order to add multiple responsibilities. As this is a controlled example (just for the purpose of this discussion), I am enforcing that only ONE responsibility is going to be extended. This means, we will have ONE decorator class. Because of that, I have made some simplifications to the design; so that you get a taste of the decorator pattern in its simplest expression.

Later on, in other post, we’ll see how to add multiple responsibilities (with multiple decorator classes). For that, we’ll need a more complex design to overcome the shortcomings of this initial example. For now, just get the idea...we'll come back later to the multiple decorations scenario.

If you get some time, take a look at the discussion in the comments section.

Subtask 1: Let’s create a TConsole class which purpose is to output a given text to the standard output. The code might be something like this:

interface
type
  TConsole = class
  public
    procedure Write(aText: string);
  end;

implementation

procedure TConsole.Write(aText: string);
begin
  Writeln(aText);
end;

Subtask 2: Let’s use the TConsole class to printout “Hello World!”. The following code snipped does it:

var
  MyConsole: TConsole;
begin
  MyConsole:= TConsole.Create;
  try
    MyConsole.Write('Hello World!');
  finally
    MyConsole.Free;
  end;
  Readln;
end.

This is how the output looks like:

Hello World!

Subtask 3: Now, let’s decorate the object referenced by MyConsole (only that object, not the whole class). What I want is to upper case every text to be printed out. We need to define a decorator class TUpperCaseConsole for that purpose. See the code:

interface

uses
  SysUtils;

type
  TConsole = class
  public
    procedure Write(aText: string); virtual;
  end;


  //Decorator
  TUpperCaseConsole = class(TConsole)
  private
    FConsole: TConsole;
  public
    constructor Create(aConsole: TConsole);
    destructor Destroy; override;

    procedure Write(aText: string); override;
  end;

implementation

{ TConsole }

procedure TConsole.Write(aText: string);
begin
  Writeln(aText);
end;

{ TUpperCaseConsole }

constructor TUpperCaseConsole.Create(aConsole: TConsole);
begin
  inherited Create;
  FConsole:= aConsole;
end;

destructor TUpperCaseConsole.Destroy;
begin
  FConsole.Free;
  inherited;
end;

procedure TUpperCaseConsole.Write(aText: string);
begin
  aText:= UpperCase(aText);
 
FConsole.Write(aText);
end;

Notice in the code above that the decorator class (TUpperCaseConsole) inherits from the decorated class (TConsole). This makes both the decorated and the decorator objects to share the same public interface. Furthermore, the TUpperCaseConsole class Has-A field of the TConsole type. We’ll use this field to forward the printing functionality to the TConsole class, once we have applied the cosmetic (upper case transformation) to the text.

Subtask 4: Let’s now create some consuming code to decorate one TConsole object on the fly. Note how the TUpperCaseConsole constructor wraps (decorates) the object referenced by MyConsole.

var
  MyConsole: TConsole;
begin
  MyConsole:= TConsole.Create;
  MyConsole:= TUpperCaseConsole.Create(MyConsole);
  try
    MyConsole.Write('Hello World!');
  finally
    MyConsole.Free;
  end;
  Readln;
end.

This is how the output looks like after the decorator has been applied:

HELLO WORLD!  

In real life, you’ll have to judge whether the decorator pattern is the best alternative to be applied to solve a particular problem. Not always it the right way to go with. For more details get your hands on these classics books.
Design Patterns: Elements of Reusable Object-Oriented Software

Head First Design Patterns

Object Models: Strategies, Patterns, and Applications (2nd Edition)

16 comments:

  1. This may be one of rare cases when it's actually OK to free object that was allocated outside the TUpperCase class in the class' destructor. However, the result is very unreadable and unintuitive. It would be better to design TConsole and TUpperCase with interfaces.

    Plus I would rename TUpperCase to TUpperCaseConsole.

    ReplyDelete
  2. Hi gabr,

    I see your point about the weirdness; nonetheless, I decided to leave the interfaces out until I write about multiple decorators. At that moment, I'll introduce a common interface for the console class and its decorators. I want to keep this simple. Newcomers to Delphi don't cope well with interfaces...

    About renaming the TUpperCase class...wouldn’t you prefer TUpperCaseDecorator instead?

    Thanks for your feedback.

    ReplyDelete
  3. Not really. I think that the class name should represent the functionality as seen from the programmer's perspective. In this case, TUpperCase is a console that outputs everything in upper case. I don't really care if it is implemented as a decorator or not.

    ReplyDelete
  4. Yes, but this post is intended for educational purposes, not for real production. I wanted to stress which class plays as decorator and which one is been decorated.

    ReplyDelete
  5. I take your point. I can add comments to the code so that the roles are understood. (Updating the code above to reflect your suggestion)

    Thanks Gabr.

    ReplyDelete
  6. Think you have a bug in your TUpperCaseConsole.Write code: Instead of "inherited Write(atext)", don't you really want to say "fconsole.Write(atext)"? You get the same effect in both cases here, but if you start decorating objects that lie at a different depth in the inheritence tree, you're going to get bit 'cause you're calling the base class' Write() method instead of the decorated object's Write() method.

    ReplyDelete
  7. Oh my gosh!!! Erik, you are absolutely right. I don't even know what I was thinking...in fact, I was not thinking at all :-)

    I'll update the code. Thanks a LOT for your feedback.

    ReplyDelete
  8. Actually the decorator pattern is not a wrapper even if some people may say this. The adapter pattern is, because the adapter does not involve inheriting from the adapted class while the decorator does.

    Personally I also think this example shows the bad things about this pattern. You create heavy dependencies (inheriting) although you wanna solve some totally different problem. Writing in uppercase has nothing to do with the the output target whatsoever. In fact - and this is very important imo - the decorator only should be used if you cannot modify the decorated class for any reason.

    ReplyDelete
  9. I see your point Stefan, thanks for your comments.

    Anyway, I am one of those who like to call it wrapper. When using the pattern we are enveloping the object with the main functionality (printing), within another object that adds some cosmetics (upper casing). Despite of how everything is tied up inside, this looks like an envelope (wrapper, skin) to me.

    Yes, this example is silly. I wanted the example silly to avoid any extra complexity in understanding the relationships within classes and objects. As I said, "In real life, you'll have to judge whether the decorator pattern is the best alternative to be applied to solve a particular problem." My example is not real life of course.

    Finally, I don't agree with this: "the decorator only should be used if you cannot modify the decorated class for any reason." There are many situations in which the decorator pattern comes naturally. Take for instance this classic example from GoF:

    "Suppose we have a TextView object that displays text in a window.TextView has no scroll bars by default, because we might not always need them.When we do, we can use a ScrollDecorator to add them. Suppose we also want to add a thick black border around the TextView. We can use a BorderDecorator to add this as well. We simply compose the decorators with the TextView to produce the desired result."

    Don't you think the decorator pattern is the proper way to go with the example above?

    ReplyDelete
  10. The GoF example actually is stupid imo and only valid if you cannot change the textview class. What would you do when you want scrollbars AND thick black border? If you would design this from the ground up you would build those 2 things into the textview. Or make it some aggregation. But not use the decorator.

    Actually the decorator to me only is a solution when someone did not follow the LSP and OCP and using it actually may lead to violating those two principles.

    ReplyDelete
  11. By calling the GOF example stupid (in your opinion), you are actually implying that Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides were (are) stupid as well. More than that, in your opinion, I am stupid as well: no hard feelings, that doesn't bother me. This make me think that you are either completely right or just the opposite, completely wrong. Please, go on with your argument. I think we can learn from this.

    One question based on what I understood about your comments: Do you consider the decorator an anti-pattern instead of a pattern?

    I wouldn't build everything into the textview class. There is a simple reason: sometimes you can't foresee everything that's coming in the future (even if you can, why you would do so?). Besides, how would you attach/remove functionality to a set of objects DYNAMICALLY? If you build everything within the textview class, pretty much everything will be static (at compilation time).

    I can't simply express myself better than GOF about this:

    "Decorator offers a pay-as-you-go approach to adding responsibilities. Instead of trying to support all foreseeable features in a complex, customizable class, you can define a simple class and add functionality incrementally with Decorator objects. Functionality can be composed from simple pieces. As a result, an application needn't pay for features it doesn't use. It's also easy to define new kinds of Decorators independently from the classes of objects they extend, even for unforeseen extensions. Extending a complex class tends to expose details unrelated to the responsibilities you're adding."

    Again Stefan, your comments are welcome and appreciated.

    ReplyDelete
  12. Maybe stupid was the wrong word here - let me replace this with "not according to what my understanding on the clean code principles and writing testable code is". By the way the singleton pattern is also a GoF pattern. Yes people use this but from a clean code point of view this thing is just horrible.

    But I have to admit that I was wrong. I was mislead by your example but I took another look at how the decorator pattern really is defined. The Decorator does NOT inherit from the concrete class but from an abstract one (or interface). So there are no dependencies to concrete classes that you can extend. (look at the class diagrams again) And this makes a huge difference and suddenly what I said is wrong because this actually does not violate the LSP or OCP.
    So in your case you would have some IConsole interface or some TConsole which does not have an implementation for Write but you would inherit one which does.

    ReplyDelete
  13. Now, we agree Stefan. Look at the second comment in this post: "I decided to leave the interfaces out until I write about multiple decorators. At that moment, I'll introduce a common interface for the console class and its decorators".

    Note that I was coding for a very special case: a single decoration (one decorator). If more decorators were needed, then the IConsole will be there.

    This is what GOF says about that:

    "Omitting the abstract Decorator class. There's no need to define an abstract Decorator class when you only need to add one responsibility."

    ReplyDelete
  14. If you didn't want to use interfaces (not unreasonably, given Delphi interfaces aren't 'pure' interfaces), then perhaps using an abstract base class would have been better. This would lead to 3 classes: a TConsole with an abstract Write method, a TConsoleImplementation with the standard Write implementation, and a TUpperCaseConsoleDecorator (inheriting from TConsole not TConsoleImplementation) implementing Write by delegation.

    That said, like Stefan, I don't think the 'TextView' example is a good one - in fact, I'm even inclined to agree it is a 'stupid' one, whatever the merits of its suggestors' substantive arguments. Bluntly, in order to support scroll bars well, a visual framework needs to be doing it as a core feature, not as some sort of 'bolt on'. Imagine if the TMemo control didn't support them natively - trying to add them in with a decorator would require completely redoing TMemo's own native painting.

    IMO, a decorator as you have described it only makes sense if the thing being added has an implementation truly independent from what it's being added to. You own example much better demonstrates this than the 'TextView' one, since how text is upper cased is unconected to how you go about outputting text as such to the screen.

    ReplyDelete
  15. I agree that some examples have to be kept simple. Anyhow try to unit test your decorator. It is impossible because it directly depends on the concrete implementation. Also how can you assure that not someone else needs to write another decorator (lowercase anyone?) and you already have 2 heavy dependencies and untestable code.

    Examples have the problem that people look at them and then go and try it out on their own. And if there is nowhere (except the comments) mentioned that this is some special case (only having one decorator) and that the "normal" case would involve writing an abstract base class that could lead to problems.

    As I mentioned already the non abstract base class approach more looks like some legacy code workaround than some clear design for something new.

    ReplyDelete
  16. CR, Stefan, thank you for your comments.

    It's true, how can I ensure that I will need just one decorator? I can ensure that, because this is my example (I can put the artificial restrictions I want). Furthermore, as I said in the beginning, it is a simplistic (silly) example :-)

    It was never my intention to leave this example as it is. My intention is to write a second post in which I will add a second decorator (maybe a third). At that moment, the introduction of the interface (or the abstract class) will be enforced.

    I'll do as you said CR: I will better use an abstract class.

    Stefan, I did mention in the body of the post that this is a particular case: "this example will extend just ONE responsibility (functionality). This means, we will have one decorator class. Later on, in other post, we’ll see how to add multiple responsibilities (with multiple decorator classes). For that, we’ll need a more complex design."

    Anyhow, Stefan, I will update that paragraph later today to stress the problems that might arise.

    Thanks again: good stuff.

    ReplyDelete