A Design Pattern A Day: The Decorator Pattern

Last week I started reading the Gang Of Four book for about the fifth time. Unfortunately I’m really bad at reading reference-like books. I always get bored after reading a few design patterns and put the book on my shelf to collect dust again. This time I’ve decided to do something different, because everybody knows:

If you keep doing what you’re doing, you’ll keep getting what you’re getting

So I decided that each design pattern that I read somewhere, be it on Twitter, on a blog
post or somewhere else I’m going to look up in the book of the Gang of Four, implement it and blog my implementation. Although the title of the series is ‘A Design Pattern A Day’, don’t take it literally. I will try to implement a design pattern as often as possible, but it won’t be daily. I just liked the name.

Because C# is what I’m still most fluent at, everything will be implemented in C# using the .Net framework 4.0. All implementations can be found on a public Github repository and we’re are all responsible programmers so all code is covered with unit tests written in MSpec. To get started with MSpec, read this excellent post by my ex-colleague Jan on Elegant Code.

The first design pattern I came across was the Decorator pattern. Let’s start with the definition of the pattern, what can it be used for? From the Gang Of Four book:

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

I’m not going to type out the whole explanation what the Decorator pattern consists of. You can read all about it on its Wikipedia page. If you’re that kind of guy, here is an UML schema for you which should explain it. Or mess things up even further if you’re that other kind of guy. Don’t worry, it will all click together if you see the implementation.

Decorator UML class diagram

We’re going to use the Decorator pattern to measure and adapt the speed of a running dog. So we’re starting out with our base component, the Animal class which has a Run method. The Run method returns the speed in miles per hour the animal is running at. We can reuse this component class in the future to measure the speed of other animals.

public abstract class Animal
{
	public abstract int Run();
}

We create our Dog class which inherits from Animal, a Dog is an Animal after all, and implement the Run method. After googling for 2 seconds I found out that a dog runs 25 miles per hour.

public class Dog : Animal
{
	public override int Run()
   	{
		return 25;
    }
}

But alas, our dog doesn’t have a lot of luck in his life. He is hit by a car and becomes handicapped. Because of his handicap he can only run a third as fast as he could when he still had all his legs. We could subclass Dog an create a HandicappedDog but what if the next animal we get is a Pony and he gets hit by a car? Then we have to create a HandicappedPony which will have the same logic as the HandicappedDog class. And for the smart-asses: yes, when a pony loses a leg or a dog loses a leg they both lose a third of their original speed. I’m sorry but that’s just how things work in the real world.

Although the duplication is minimum, we still want to avoid it and instead of subclassing our Dog class we create a Decorator. We have a generic Decorator which allows us the quickly create a new Decorator.

public abstract class AnimalDecorator : Animal
{
	protected Animal Animal;

    protected AnimalDecorator(Animal animal)
   	{
   		Animal = animal;
   	}
   
  	public override int Run()
	{
		return Animal.Run(); 
	}
}

We create a HandicappedDecorator which returns a third of the speed of the original Animal it is ‘decorating’. If we create a handicapped dog, you will notice that the speed of the handicapped dog is only 8 miles per hour.

public class HandicappedDecorator : AnimalDecorator
{
	public HandicappedDecorator(Animal animal)
		: base(animal)
	{
	}

	public override int Run()
	{
		return base.Run() / 3;
	}
}

In the test of the HandicappedDecorator you can see how to use this Decorator:

[Subject("Measuring the speed of a running handicapped dog")]
public class When_measuring_the_speed_of_a_running_handicapped_dog
{
	Establish context = () =>
	{
		_classUnderTest = new HandicappedDecorator(new Dog());
	};
   
	Because of = () =>
		_measuredSpeed = _classUnderTest.Run();
   
	It should_have_a_third_of_the_speed_of_a_normal_running_dog = () =>
	{
		var normalSpeed = new Dog().Run();
		_measuredSpeed.ShouldEqual(normalSpeed/3);
	};
   
	private static HandicappedDecorator _classUnderTest;
	private static int _measuredSpeed;
}

But we miss the times that our dog ran 25 miles per hour and we buy him a pair of bionic legs to augment its speed. The bionic legs double the speed of the dog, so a normal dog will be running at 50 miles per hour.

public class BionicLegsDecorator : AnimalDecorator
{
	public BionicLegsDecorator(Animal animal) : base(animal)
    {
	}
   
	public override int Run()
	{
		return base.Run() * 2;
	}
}

The cool thing about this implementation is that we can ‘chain’ the decorators, so we can make a dog handicapped an give him a pair of bionic legs. So the handicapped dog runs at 16 miles per hour.

[Subject("Measuring the speed of a running handicapped dog with bionic legs")]
public class When_measuring_the_speed_of_a_running_handicapped_dog_with_bionic_legs
{
	Establish context = () =>
	{
		_classUnderTest = new BionicLegsDecorator(new HandicappedDecorator(new Dog()));
	};

	Because of = () =>
  		_measuredSpeed = _classUnderTest.Run();

	It should_have_a_speed_of_16_miles_per_hour = () =>
  		_measuredSpeed.ShouldEqual(16);

	private static BionicLegsDecorator _classUnderTest;
	private static int _measuredSpeed;
}

So that was the Decorator pattern I hope you learned something, if you want to check out all code with tests, head over to the public Github repository. I found another implementation of the Decorator pattern in C# if you still want to know more.

And remember, a design pattern a day keeps the all-nighters away … Or something like that. As always, feedback, remarks? All welcome in the comments.