Following up on the Observer/Event Pattern, it's time for the third pattern, the Decorator Pattern.

The definition: "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."

Continuing on our little game, let's say we are able to create buildings and have just an IBuilding interface and one building.

Simple Building

Each building has a description and a given power usage cost, which is simply hardcoded in the class right now.

After a while, our boss hired an external company to take over development with regards to the buildings and tasked them with making buildings upgradeable.

The first approach they took was too simply add new building classes to the game, for each possible upgrade.

Class Explosion

This however leads to an infinite amount of possibilities and a nightmare to maintain.

If we look at the definition of the Decorator Pattern, it seems to offer a good alternative to all this inheriting to add in new functionality.

Also, adding additional responsibilities dynamically means we don't have to change the code of the IBuilding and NormalBuilding either. By doing this we are honoring the Open/Closed Principle, which says "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".

How are we going to add these additional things dynamically? We'll decorate our initial NormalBuilding with upgrades, giving us flexibility to equip a building with any upgrade combination we like.

When we mention decorating, think about wrapping. We are going to wrap an instance of IBuilding in a decorator, acting as a shell around the class, passing calls to our decorator along to the wrapped instance, modifying the results when needed.

It's also possible to chain decorators together, making it possible to decorate a NormalBuilding with two Turrets, a SAM installation and a Laser. This is done because a decorated object is still of the type IBuilding, and can be decorated again and again.

Eventually we'll end up with the following class diagram. Note, the WrappedBuilding has a protected getter and a private setter.

Decorator Pattern

Now we have a set of buildings, which implement IBuilding and one decorator base class, BuildingUpgradeDecorator, from which all possible decorations inherit. This decorator class has to implement IBuilding as well, so it can be re-decorated.

With the above classes, it's possible to compose buildings as we desire, as shown below.

Decorator Usage

When we run this code, the call to the Power property is being passed through all decorators to the lowest level, our NormalBuilding, and returned all the way back, getting incremented with the additional power usage for each decorator/upgrade in the mean time.

Decorator Output

I've uploaded the solution again so you can have a look on how the decorator base class and individual decorators are programmed.

Some additional information on the Decorator pattern:

 
Comments: 7
 
  • Thanks for the simple but great posts David. Please keep them coming. :)

     
     
  • Nice article!

     
     
  • Arch4ngel

    If you keep posting stuff like this, I'll get hooked and will have to watch every morning if you didn't post something interesting.

    I already bookmarked you. Keep posting! You've become part of my daily routine!

    Cheers!

     
     
  • Thanks :) It'll be a little slower this week, I'm currently in London for work, with little time to read and write :(

    But at least I'll be back in Belgium next week :)

     
     
  • Harish

    Great posting on patterns , easier to digest .
    Can we get an index please ?

     
     
  • An index like this: http://blog.cumps.be/tags/design-patterns/ ?

    Or just an overview of what is yet to come?

     
     
  • Hi David, I just wanted to say thanks again for the crisp, clean & downloadable examples. It really helps hit the idea home -- hope you keep posting!

     
     
  • Leave a reply
    Items marked with * are required. (Name, Email, Comment)
    Comment is missing some required fields.
     
     
     
    To make sure you are not a computer, please type in the characters you see.