Learning design patterns with Actionscript 3: episode 2 – Decorator pattern

The objective of the decorator pattern is to provide a way to add extra features and responsibilities to a class at runtime without to subclass it.

The classes diagram of this pattern is composed by an interface or an abstract class which will define the supertype of the class that will be extended (decorated) by one or more classes implementing the same interface and extending an abstract class which identify these as decorator classes.

A real scenario into which apply this pattern can be a cars store. A car is the base class and the accessories such dvd player, gps, air conditioning and so on, can be thought as decorators. Once decorated, the car (which has a method getPrice() which returns its cost) will get updated by accessories, so calling the getPrice() method will include the price of the “naked” car plus the price of the various accessories installed.

Let’s take a look to the classes…

package com.mycarstore {
	
    public interface IVehicle {
		
        function getPrice():Number;
        function getDescription():String;
		
    }
	
}

The interface above will be implemented by all the classes and we will treat the car as an IVehicle not a concrete Car class.
This is our simple Car class:

package com.mycarstore {
	
    public class Car implements IVehicle {
		
        public function Car() {
        }

        public function getPrice():Number {
			
            return 50000;
			
        }
		
        public function getDescription():String {
			
            return "Base car";
			
        }
		
    }
	
}

Now is time to see the decorators (in our scenario the accessories). This is the abstract Accessory class, all accessories will extend it:

package com.mycarstore {
	
    public class Accessory implements IVehicle {
		
        protected var _vehicle:IVehicle;
		
        public function Accessory(vehicle:IVehicle) {
			
            this._vehicle = vehicle;
			
        }
		
        public function getPrice():Number {
			
            return this._vehicle.getPrice();
			
        }
		
        public function getDescription():String {
			
            return this._vehicle.getDescription();
			
        }
		
    }
	
}

In Accessory we can notice that the class encapsulate an object of IVehicle which is passed to the constructor (of course since this class is abstract the object will be passed to the constructor of Accessory’s subclasses). This is the key concept of decorator pattern, all decorators keep a reference of the interface they are going to decorate and this reference is used as the base for methods business logic. Let’s see how:

package com.mycarstore {
	
    public class DVDPlayer extends Accessory {
		
        public function DVDPlayer(vehicle:IVehicle) {
			
            super(vehicle);
			
        }
		
        override public function getPrice():Number {
			
            return this._vehicle.getPrice() + 550;
			
        }
		
        override public function getDescription():String {
			
            return this._vehicle.getDescription() + ", DVD Player";
			
        }
		
    }
	
}

DVDPlayer is a concrete decorator and it uses the encapsulated IVehicle object in order to provide an extended version of the interface methods (getPrice() and getDescription()). Basically it uses first the reference methods and then it adds some other values.

To use the pattern we will first instantiate a Car object, than we will wrap it with one or more decorators:

var car:IVehicle = new Car();
				
trace("Price: " + String(car.getPrice()) + " - description: " + String(car.getDescription()));
				
car = new DVDPlayer(car);
				
trace("Price: " + String(car.getPrice()) + " - description: " + String(car.getDescription()));
				
car = new GPSSystem(car);
				
trace("Price: " + String(car.getPrice()) + " - description: " + String(car.getDescription()));

The output will be:

Price: 50000 - description: Base car
Price: 50550 - description: Base car, DVD Player
Price: 50875 - description: Base car, DVD Player, GPS System

The order into which we wrap the concrete component (Car) is indifferent (we can add GPSSystem before or after DVDPlayer) and of course despite its readability, we can wrap the Car in a single statement:

var car2:IVehicle = new GPSSystem(new DVDPlayer(new Car()));

In this example I used very simple classes, however in a real implementation we can create more sophisticated decorators which will accept extra arguments beyond the IVehicle reference.

  • Ciao Davide,
    thank you for this insight. Good example and clear code.
    Keep up the good work!

  • Ciao Danilo and thank you, I will publish more post about patterns in the future. Design patterns are very powerful and they are treated in many blogs/books but the problem is that is difficult to find concrete examples and real world implementation… and this is my goal :)

  • mysliwy

    Thx ;)