BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Design Patterns
.NET 1.1+

Decorator Design Pattern

The decorator pattern is a design pattern that extends the functionality of individual objects by wrapping them with one or more decorator classes. These decorators can modify existing members and add new methods and properties at run-time.

Example Decorator

Earlier in this article I described an example use of the decorator pattern for a track-day hire system. In the remainder of the article we will implement the skeleton of such an object model. In the example we will define a vehicle class that holds details of a car, its hire charge and number of laps permitted. We will also create two decorator classes. The first decorator will be used to describe special offers by modifying the lap count and price. The second will add the method required to log the details of a hire period. We will then demonstrate the effects of the two wrappers.

The code involved in creating the entire functionality for this object model would be rather large so it will be limited to outputting messages to the console describing the state and behaviour of the objects. The code for the base classes and a concrete vehicle class is as follows. Note that C# 3.0 automatically implemented properties syntax has been used for brevity. For earlier versions of the language you will need to implement the properties and backing variables in full:

public abstract class VehicleBase
{
    public abstract string Make { get; }
    public abstract string Model { get; }
    public abstract double HirePrice { get; }
    public abstract int HireLaps { get; }
}


public class Ferrari360 : VehicleBase
{
    public override string Make
    {
        get { return "Ferrari"; }
    }

    public override string Model
    {
        get { return "360"; }
    }

    public override double HirePrice
    {
        get { return 100; }
    }

    public override int HireLaps
    {
        get { return 10; }
    }
}


public abstract class VehicleDecoratorBase : VehicleBase
{
    private VehicleBase _vehicle;

    public VehicleDecoratorBase(VehicleBase vehicle)
    {
        _vehicle = vehicle;
    }

    public override string Make
    {
        get { return _vehicle.Make; }
    }

    public override string Model
    {
        get { return _vehicle.Model; }
    }

    public override double HirePrice
    {
        get { return _vehicle.HirePrice; }
    }

    public override int HireLaps
    {
        get { return _vehicle.HireLaps; }
    }
}

Special Offer Decorator

The first decorator class will allow a special offer to be applied to the hiring of a vehicle. The class will include two additional properties to those in the base class. These will define a discount percentage and a value that increases the number of laps that may be driven during a hire period. The HirePrice and HireLaps properties will be overridden to include these modifiers. The code for the decorator is as follows:

public class SpecialOffer : VehicleDecoratorBase
{
    public SpecialOffer(VehicleBase vehicle) : base(vehicle) { }

    public int DiscountPercentage { get; set; }
    public int ExtraLaps { get; set; }

    public override double HirePrice
    {
        get
        {
            double price = base.HirePrice;
            int percentage = 100 - DiscountPercentage;
            return Math.Round((price * percentage) / 100, 2);
        }
    }

    public override int HireLaps
    {
        get
        {
            return base.HireLaps + ExtraLaps;
        }
    }
}

Hireable Decorator

The second vehicle decorator will not modify the underlying properties of the vehicle object it wraps. Instead, it will add a new method that records a hire period. To demonstrate the Hire method, the class will output the name of the person hiring the vehicle and the details of the vehicle, price and number of laps purchased to the console.

public class Hireable : VehicleDecoratorBase
{
    public Hireable(VehicleBase vehicle) : base(vehicle) { }

    public void Hire(string name)
    {
        Console.WriteLine("{0} {1} hired by {2} at a price of {3:c} for {4} laps."
            , Make, Model, name, HirePrice, HireLaps);
    }
}
15 February 2009