Home  >  

Decorator Design Pattern

Author photo
| | Comments (6)
AddThis Social Bookmark Button

Overview

Purpose of this article

The goal of this article is to help you gain a better understanding of the Decorator design pattern.  The Decorator pattern is used to attach additional responsibilities to an object at run time dynamically. Decorators provide a flexible alternative to subclassing to extend the functionality of classes in a hierarchical relationship. The Decorator design pattern utilizes an important OOP concept known as the open closed principal. This means that classes are open to extension but closed for modification. The Decorator pattern also uses the concept of composition, another very powerful OOP concept.

Design patterns are an awesome topic and the Decorator pattern is very powerful. Let’s continue exploring!
-Sean Moore, August 2009

Prerequisite knowledge

OOP fundamentals

A firm handle on basic OOP fundamentals is important to understanding this article. The Decorator design pattern uses core OOP concepts such as inheritance and composition.

OOP Links:

If you are not abreast in OOP basics you should take a moment to review the following material:

Object-oriented programming with ActionScript 3.0
http://www.adobe.com/devnet/actionscript/articles/oop_as3.html

Object Orientation
http://www.tricedesigns.com/tricedesigns_home/blog/2006/12/object-orientation.html

Lesson: Object-Oriented Programming Concepts
http://java.sun.com/docs/books/tutorial/java/concepts/

Decorator pattern examples

There is an example Flex project to going along with this article. The class diagrams have also been included for in the accompanying download.

This example demonstrates the Decorator pattern in practice. We’ll be building up a User class with several different configurations that different user types could possess.

Code example:
http://www.seantheflexguy.com/flex/insideria/DecoratorDesignPattern.zip

UML Diagrams:
http://www.seantheflexguy.com/flex/insideria/flex-decorator-uml-diagrams.zip

Decorator pattern concepts

Decorator pattern official definition:

Here is the definition of the Decorator design pattern from the Gang of Four’s book:

“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.” - Design Patterns: Elements of Reusable Object-Oriented Software

This definition begins to explain things but we’ll be taking a deeper look into the pattern in this article. Decorator is a good pattern to learn after learning the Strategy pattern. The Factory, Composite and Iterator are all great patterns to follow up the Decorator pattern with.

Core concepts and usage

The Gang of Four book describes three families of design patterns: creational patterns, structural patterns and behavioral patterns. The Decorator design pattern is a structural pattern.

“Structural patterns are concerned with how classes and objects are composed to form larger structures.” - Design Patterns: Elements of Reusable Object-Oriented Software

The Decorator pattern uses recursive composition to add layers of functionality to objects. Composition is a very important OOP concept to understand. Inheritance is a great way to build class hierarchies however it isn’t always the best or only solution. If implemented incorrectly or without taking a moment to consider all of the possible objects that can be born you create the opportunity for a class explosion. A multitude of classes created using inheritance can become a gigantic maintenance nightmare. The Decorator pattern can help prevent this problem from happening by using composition over inheritance.

A Decorator class follows the rules defined by the interface of the object it is enhancing. In doing so the objects that use the decorator aren’t aware that it is not truly an instance of the class being decorated. The decorator class passes along method calls to the class it’s enhancing. This approach enables a very flexible structure that permits an infinite amount of functionality that can be added to the class being decorated. Additional functionality can be added that isn’t tied to the decorated classes interface. You can supply various Decorators to Component classes to mix and match functionality.

Open Closed Principle

The Decorator Design pattern uses what’s known as the Open Closed Principle. This concept states that classes should be open for extension but closed for modification. In the decorator pattern composition is used to achieve this functionality. The decorator classes store an instance of the class they are decorating and forward method calls to that instance. The decorator adds more functionality as needed without having to modify the code of the instance it’s holding. This is a powerful concept that prevents from having to make changes to the class the decorator holds. Making changes to the class could potentially cause issues for a codebase that has already been tested and released. Utilizing the Open Closed Principle can help prevent ramifications from making such changes.

More information can be found here:
http://en.wikipedia.org/wiki/Open/closed_principle

Applicability

The Decorator pattern can be utilized in the follow scenarios:

  • Attach functionality to individual objects dynamically and transparently.
  • Add functionality to classes that can be removed.
  • Prevent class explosion.

Core pattern diagram

The Decorator pattern can be seen below in its purist form. This is the design pattern with no implementation. This is basically the same diagram that is presented in the GoF book. It may help to review the concrete implementations of the pattern presented in this article and then return to review the core pattern diagram to understand the abstract concept.

Decorator-Design-Pattern-diagram.jpg

Pattern participants

Here is a quick overview of the main participants of the Decorator design pattern:

  • Component

The interface for objects that dynamically have new functionality added to them

  • ConcreteComponent

An object that new functionality can be attached

  • Decorator

Stores a reference to a Component object and mirrors the Component classes interface

  • ConcreteDecorator

Builds additional functionality on top of the Component class

Flex Decorator design pattern example

Sample Decorator pattern implementation

To gain a better understanding of a pattern it’s often a good idea to create a code example. The accompanying code example is simple yet practice on a very basic level. It doesn’t deal with fruit, or animals or cars. Just keep in mind this is a very basic example that will hopefully shed some light on the underlying concepts used by the pattern.

The example adds different types of web hosting options to a base web hosting plan and calculates a total price depending on what services have been added on top of the base plan. Hopefully this concept is familiar enough to make sense without distracting from the design pattern concepts being explained.

Sample Decorator classes

This example is made up of the following classes:  

  • DecoratorDesignPattern

Creates the instance of the Order class and decorates it with instances of different types of hosting services.

  • IOrder

Defines the contract that implementing classes such as Order must follow. The abstract decorator and Order classes implement the IOrder interface. Other objects that use Order objects or concrete decorators don’t know the difference between an Order and a decorated Order.

  • Order

A concrete implementation of the IOrder interface that will be decorated with the various hosting service classes.

  • AbstractOrderDecorator

Provides the basic functionality for the concrete decorator objects.

  • BasicHostingServiceCost

Provides functionality and pricing details for the basic hosting service decorator.

  • FlashMediaServerHostingServiceCost

Provides functionality and pricing details for the Flash Media Server hosting service decorator.

  • MySQLHostingServiceCost

Provides functionality and pricing details for the MySQL hosting service decorator. This class adds additional functionality to add databases to the account and increase the hosting cost accordingly.

  • SubversionHostingServiceCost

Provides functionality and pricing details for the Subversion hosting service decorator.

Decorator-Design-Pattern-diagram-2.jpg

Running the Decorator Design Pattern example

When the Decorator Design Pattern example is launched an Order is created and various services are added to the base hosting account increasing the cost. Trace statements are executed sending messages to the Output window showing the services being added and the price being increased for each service.

The IOrder Interface

Here is the contract that defines all Order objects. This interface is pretty straight forward. It defines two methods that all implementing classes must have. Consequently the abstract decorator and decorator implementations will also follow this contract.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
       public interface IOrder 
       { 
              function calculateTotal() : Number 
              function getBilledServices() : String 
        } 
}
This is the interface that defines the contract for the concrete Order class to follow.

The concrete Order class

Here is the concrete implementation of the Order class.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
       public class Order implements IOrder 
       { 
              public function Order() 
              { 
               } 
              
              public function calculateTotal() : Number 
              { 
                     return 0; 
               } 
              
              public function getBilledServices() : String 
              { 
                     return "Billed services: "; 
               } 
        } 
}
The implementation details of this class are pretty generic for this example. This is the base class that will be decorated with hosting service options.

The AbstractDecorator class

Here is the code for the abstract representation of a web hosting service decorator.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
       public class AbstractOrderDecorator extends Order 
       { 
              
              protected var order : Order; 
              
              public function AbstractOrderDecorator() 
              { 
               } 
              
              override public function calculateTotal() : Number 
              { 
                     return order.calculateTotal(); 
               } 
              
              override public function getBilledServices() : String 
              { 
                     return order.getBilledServices(); 
               } 
        } 
}
Notice that this class extends the Order class and overrides the calculateTotal and getBilledServices methods. Also take note that this class contains an instance of an Order object and forwards the method calls to the Order instance. Take a moment to review the core pattern diagram to see how this all is all fitting together.

The BasicHostingServiceCost class

Here is the concrete implementation for the basic hosting service cost class.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
       public class BasicHostingServiceCost extends AbstractOrderDecorator 
       { 
              
              private var serviceName : String; 
              private var totalCost : Number; 
              
              public function BasicHostingServiceCost( order : Order ) 
              { 
                     this.order = order; 
                     totalCost = 3.99; 
                     serviceName = "Basic Hosting"; 
               } 
              
              override public function calculateTotal() : Number 
              { 
                     return order.calculateTotal() + totalCost; 
               } 
              
              override public function getBilledServices() : String 
              { 
                     return order.getBilledServices() + serviceName; 
               } 
        } 
}
The first concrete implementation of the abstract hosting service is pretty simple. Notice that the constructor takes an Order object as a parameter. The constructor uses the order passed to it to populate this class’s order property. The serviceName and totalCost properties are also set to values specific to this decorator.  Also notice that the calculateTotal and getBilliedServices methods call the order instances methods and add specific data for this decorator.

The SubversionHostingServiceCost class

Here is the concrete implementation for the subversion hosting service cost class.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
        public class SubversionHostingServiceCost extends AbstractOrderDecorator 
        { 
                
                private var serviceName : String; 
                private var totalCost : Number; 
                
                public function SubversionHostingServiceCost( order : Order ) 
                { 
                         this.order = order; 
                         totalCost = 5.99; 
                         serviceName = "Subversion Hosting"; 
                 } 
                
                override public function calculateTotal() : Number 
                { 
                         return order.calculateTotal() + totalCost; 
                 } 
                
                override public function getBilledServices() : String 
                { 
                         return order.getBilledServices() + ", " + serviceName; 
                 } 
         } 
} 
This class is another decorator for the Order class. Notice that the service name and total cost properties have varied from the basic hosting service class. The same traits of the basic hosting service class are present. The constructor accepts an Order object as a parameter and populates the subversion hosting class’s order property with the parameter value. Method calls to calculate and get the billed services are forwarded to the order instance.

The FlashMediaServerHostingServiceCost class

Here is the concrete implementation for the Flash Media Server hosting service cost class.

 
package com.seantheflexguy.insideria.dp.decorator 
{ 
        public class FlashMediaServerHostingServiceCost extends AbstractOrderDecorator 
        { 
  
                private var totalCost : Number; 
                private var serviceName : String; 
                
                public function FlashMediaServerHostingServiceCost( order : Order ) 
                { 
                         this.order = order; 
                         totalCost = 19.99; 
                         serviceName = "Flash Media Server Hosting"; 
                 } 
                
                override public function calculateTotal() : Number 
                { 
                         return order.calculateTotal() + totalCost; 
                 } 
                
                override public function getBilledServices() : String 
                { 
                         return order.getBilledServices() + ", " + serviceName; 
                 } 
         } 
} 
At this point you should notice that the decorators are very similar. The total cost and service name have changed for the Flash Media service hosting class but not too much else. The next decorator tailed for MySQL hosting costs will demonstrate how additional functionality can be added to decorators to enhance the functionality of the class being decorated; the Order class in this example.

The MySQLHostingServiceCost class

Here is the concrete implementation for the MySQL hosting service cost class.

package com.seantheflexguy.insideria.dp.decorator
{ 
       public class MySQLHostingServiceCost extends AbstractOrderDecorator 
       { 
              
              private var databaseCount : int; 
              private var serviceName : String; 
              private var _totalCost : Number; 
              
              public function MySQLHostingServiceCost( order : Order ) 
              { 
                     this.order = order; 
                     totalCost = 9.99; 
                     serviceName = "MySQL Hosting"; 
                     databaseCount = 1; 
               } 
              
              override public function calculateTotal() : Number 
              { 
                     return order.calculateTotal() + totalCost; 
               } 
              
              override public function getBilledServices() : String 
              { 
                     return order.getBilledServices() + 
                                       ", " + serviceName + 
                                       "(w/ " + databaseCount + 
                                       " databases)"; 
               } 
              
              public function get totalCost() : Number 
              { 
                     return this._totalCost * databaseCount; 
               } 
              
              public function set totalCost( newCost : Number ) : void 
              { 
                     this._totalCost = newCost;    
               } 
              
              public function addMySQLDatabases( databaseCount : int ) : void 
              { 
                     this.databaseCount += databaseCount; 
               } 
        } 
}
The MySQL decorator follows the same format as the other decorator classes however you’ll notice that the logic to handle the total cost has been modified and an additional method to add MySQL databases has also been added. This is the real power of the Decorator Design pattern. The Order class does not need this functionality. In an inheritance relationship any classes that extended Order would receive this method. Not all orders will have MySQL databases and don’t need a method to add them. In the current set up we can choose to add databases to orders only when needed.

Exploring the DecoratorDesignPattern MXML file

This is a runnable Flex application that pulls the pieces of the Decorator pattern together.

 
<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
         creationComplete="init();"> 
         <mx:Script> 
                  <![CDATA[ 
                           import mx.collections.ArrayCollection; 
                           
                           import com.seantheflexguy.insideria.dp.decorator.BasicHostingServiceCost; 
                           import com.seantheflexguy.insideria.dp.decorator.FlashMediaServerHostingServiceCost; 
                           import com.seantheflexguy.insideria.dp.decorator.MySQLHostingServiceCost; 
                           import com.seantheflexguy.insideria.dp.decorator.Order; 
                           import com.seantheflexguy.insideria.dp.decorator.SubversionHostingServiceCost; 
                           
                           private function init() : void 
                           { 
                                     
                                     var order : Order = new Order(); 
                                     order = new BasicHostingServiceCost( order ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     order = new SubversionHostingServiceCost( order ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     order = new FlashMediaServerHostingServiceCost( order ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     order = new MySQLHostingServiceCost( order ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     MySQLHostingServiceCost( order ).addMySQLDatabases( 1 ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     MySQLHostingServiceCost( order ).addMySQLDatabases( 1 ); 
                                     trace( order.getBilledServices() + ": " + order.calculateTotal() ); 
                                     
                            } 
                           
                  ]]> 
         </mx:Script>
</mx:Application>

First the application creates an Order object. Next a Subversion account is added ad then a Flash Media Server account is added to the order. Finally a MySQL account is added to the account and then two additional MySQL databases are added to the MySQL service. The price is being updated according to pricing information inside of each decorator class. The final output of the application is as follows:

Billed services: Basic Hosting: 3.99
Billed services: Basic Hosting, Subversion Hosting: 9.98
Billed services: Basic Hosting, Subversion Hosting, Flash Media Server Hosting: 29.97
Billed services: Basic Hosting, Subversion Hosting, Flash Media Server Hosting, MySQL Hosting(w/ 1 databases): 39.96
Billed services: Basic Hosting, Subversion Hosting, Flash Media Server Hosting, MySQL Hosting(w/ 2 databases): 49.95
Billed services: Basic Hosting, Subversion Hosting, Flash Media Server Hosting, MySQL Hosting(w/ 3 databases): 59.94

Review

Putting it all together

The Decorator Design pattern is very powerful and helps to reiterate the concept of composition over inheritance. Inheritance relationships are not always the best solution and this pattern help facilitate a means to add functionality to a class without requiring all classes that extend it to have specialized methods that don’t apply to the inheriting class. The Decorator Design pattern is used for UI components in other languages to add scrollbars and border s to components. In the GoF book the sample illustrates how a text field component can be decorated with a scrollbar. Not all text will scroll so why embed the logic to handle scrollbars into the text field? This is the same concept that’s shown in the example for this article. Favor composition over inheritance and forward the method calls an instance of the class that’s being enhanced or decorated.

Summary

I hope this article has given an insight to the power of the Decorator pattern and opened the window to using more design patterns in your application development process. Codebases that take advantage of design patterns are often easier to update and maintain and they are also more scalable to future change. Definitely take a moment to review the sample applications and review the UML diagrams as well. This article combined with the diagrams and code examples should allow you to begin using the Decorator pattern in your work.

Additional Information

Web:

Decorator pattern
Wikipedia
http://en.wikipedia.org/wiki/Design_Patterns

Decorator UML diagram - GoF version
http://www.tml.tkk.fi/~pnr/GoF-models/html/Decorator.html

Books:

Design Patterns: Elements of Reusable Object-Oriented Software
Addison-Wesley Professional - Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides
http://www.informit.com/store/product.aspx?isbn=0201633612

Head First Design Patterns
O'Reilly Media, Inc.: Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates
http://oreilly.com/catalog/9780596007126/

ActionScript 3.0 Design Patterns
O'Reilly Media, Inc.: William B. Sanders, Chandima Cumaranatunge
http://oreilly.com/catalog/9780596528461/

Refactoring to Patterns
Addison-Wesley Professional: Joshua Kerievsky
http://www.informit.com/store/product.aspx?isbn=0321213351

Read more from Sean Moore. Sean Moore's Atom feed seantheflexguy on Twitter

Comments

6 Comments

Amy said:

A simple example I use all the time of the Decorator Pattern is to wrap objects in a class that implements IUID so that the same object can appear multiple times in a List based control without confusing Flex.

J.Host said:

They are also offering free website hosting for their customers. This website also helps you to create your own blog. They are also providing the website building tools. These website building tools will be more helpful for the beginners.

Very cool article.
A good way to learn decorator pattern, thank you sean

jadd said:

Hi, i'm always glad when someone investigate d.p. I would like to see more of them! The uml diagram is only a jpp or is originated from some some software? Thank you.

Pankaj said:

Excellent Article...Thanks for sharing Sean

irieb88 said:

Sweet.

A nice refresher and using AS3 :)

Thank you for reminding me of the beauty of the decorator pattern, well written and easy to follow.

-cheers-

Leave a comment


Tag Cloud

Question of the Week: Dream App

If you had an unlimited budget and unlimited resources what application would you build and why would you build it?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.