Search content within the blog

Friday, May 6, 2011

Design Pattern- Decorator Pattern

Definition

The definition of the Decorator Pattern, according to Wikipedia, is:

“The decorator pattern can be used to make it possible to extend (decorate) the functionality of a class at runtime. This works by adding a new decorator class that wraps
the original class. This wrapping is typically achieved by passing the
original object as a parameter to the constructor of the decorator when
it is created. The decorator implements the new functionality, but for
functionality that is not new, the original (wrapped) class is used.
The decorating class must have the same interface as the original class.”
The Situation

Lets say you are assigned a project that a colleague was working on. Maybe this coworker has fallen ill or has decided to leave the company, for whatever reason, this is now your responsibility. Your manager hands the project off to you and explains the details of the project and the new features that are required.

The application is a dog registration program that your city is currently using to manage all annual dog registrations. The program has been deployed for nearly a year and now the city has decided that they would like to track when a dog gets picked up by the dog catcher. Based on this history, dogs that regularly get into trouble will cost more to register to offset the cost of employing the dog catcher.

Once you take a look at the code you discover that your coworker did not write any unit tests for this application. How will you know if your changes are going to introduce bugs into the existing features? Simple, you don’t. Without unit tests, the only way to determine if something gets broken is to manually fire up the UI and test each feature and all the possible combinations that feature may entail. Not something we want to do. That’s when the Decorator Pattern comes into play. It will allow us to wrap the new functionality around existing objects so that we do not disrupt the other code using the existing objects.
The Existing Code

The existing model looks like this.
internal interface IDog {
int Id { get; set; }
string Name { get; set; }
string Breed { get; set; }
string Address { get; set; }
DateTime RegisterDate { get; set; }
int RegistrationCost { get; }
}

public class Dog : IDog {
public Dog() : this(new DogRepository()) { }

public Dog(IRepository repository) {
_repository = repository;
}

private IRepository _repository;

private int id;
public int Id {
get { return id; }
set { id = value; }
}

private string name;
public string Name {
get { return name; }
set { name = value; }
}

private string breed;
public string Breed {
get { return breed; }
set { breed = value; }
}

private string address;
public string Address {
get { return address; }
set { address = value; }
}

private DateTime registerDate;
public DateTime RegisterDate {
get { return registerDate; }
set { registerDate = value; }
}

public int RegistrationCost {
get { return 25; }
}
}

This may not be the prettiest code but it is what it is. You can chalk that up to your coworker not being as good of a programmer as you or me just being lazy with the example code.
The New Requirements

So as mentioned above, the city now wants to track how often a dog is picked up by the dog catcher to determine if the registration cost for that dog should be higher. These details will be stored in a new database table called Infractions. The corresponding model class will look like this.
public class Infraction {
public Infraction() { }

private int id;
public int Id {
get { return id; }
set { id = value; }
}

private int dogId;
public int DogId {
get { return dogId; }
set { dogId = value; }
}

private string reason;
public string Reason {
get { return reason; }
set { reason = value; }
}

private DateTime infractionDate;
public DateTime InfractionDate {
get { return infractionDate; }
set { infractionDate = value; }
}
}
The Decorator Code

Now that we know our new requirements and have defined the structure of the new information we can get to work building our decorator. We’ll create an abstract class that implements the IDog interface.

public abstract class DogDecorator : IDog {
private IDog _dog;

public DogDecorator(IDog dog) {
_dog = dog;
}
}


Next we’ll create our decorator from the abstract class that will look up the infractions and calculate the new registration cost.


public class InfractingDog : DogDecorator {
public InfractingDog(IDog dog) :
this(dog, new DogRepository()) { }

public InfractingDog(IDog dog, IRepository repository) {

base(dog);
_repository = repository;
}

private IRepository _repository;

public int Id {
get { return _dog.Id; }
set { _dog.Id = value; }
}

public string Name {
get { return _dogName; }
set { _dog.Name = value; }
}

public string Breed {
get { return _dog.Breed; }
set { _dog.Breed = value; }
}

public string Address {
get { return _dog.Address; }
set { _dog.Address = value; }
}

public DateTime RegisterDate {
get { return _dog.RegisterDate; }
set { _dog.RegisterDate = value; }
}

public int RegistrationCost {
get {
IList<Infraction> infractions =
_repository.GetInfractionsByDogId(_dog.Id);

//increase registration by $5
//for every infraction
int extraCost = infractions.Count * 5;

return _dog.RegistrationCost + extraCost;
}
}
}
Final Thoughts

What we have done is wrap our original Dog class in a decorator to add new functionality. By doing it this way we don’t have to worry about breaking any old functionality because we didn’t change the original class. I hope this has demonstrated the Decorator Pattern to you in an easy to understand way that resembles a possible real world situation.

No comments:

Post a Comment