By definition, the Decorator Pattern is used for adding additional functionality to a particular object as opposed to a class of objects. It is easy to add functionality to an entire class of objects by subclassing an object, but it is impossible to extend a single object this way. With the Decorator Pattern, you can add functionality to a single object and leave others like it unmodified.
To implement Decorator Pattern for sky.com, let us create an interface first, we named it as ITVBundle.
public interface ITVBundle
{
string Description();
double Cost();
}
To subscribe sky sports or movie channels, you have to subscribe a basic pack even you don’t want to watch them. So the first class I need to create is BasicPack which implement ITVBundle interface.
public class BasicPack : ITVBundle
{
public double Cost()
{
return 18.50;
}
public String Description()
{
return "Basic Packs";
}
}
An decorator abstract class which also implement ITVBundle interface:
public abstract class AddOnDecorator : ITVBundle
{
public abstract string Description();
public abstract double Cost();
}
The next bit is my favourite, which is to add Sports packs or Movies Pack to the Basic Pack. This works by adding a new decorator class that wraps the original class. Let’s look at the example below:
public class AddSportsPack : AddOnDecorator
{
ITVBundle _itv;
public AddSportsPack(ITVBundle tvBundle)
{
this._itv = tvBundle;
}
public override string Description()
{
return _itv.Description() + ", with Sports Pack";
}
public override double Cost()
{
return _itv.Cost() + 9.99;
}
}
From the above you can see the 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.
Here is the example on using Decorator Pattern:
BasicPack bp = new BasicPack();
AddSportsPack plan1 = new AddSportsPack(bp);
Please see the full working code:
using System;
namespace DecoratorPattern
{
public interface ITVBundle
{
string Description();
double Cost();
}
public class BasicPack : ITVBundle
{
public double Cost()
{
return 18.50;
}
public String Description()
{
return "Basic Packs";
}
}
public abstract class AddOnDecorator : ITVBundle
{
public abstract string Description();
public abstract double Cost();
}
public class AddMoviePack : AddOnDecorator
{
ITVBundle _itv;
public AddMoviePack(ITVBundle tvBundle)
{
this._itv = tvBundle;
}
public override string Description()
{
return _itv.Description() + ", with Movie Pack";
}
public override double Cost()
{
return _itv.Cost() + 9.99;
}
}
public class AddSportsPack : AddOnDecorator
{
ITVBundle _itv;
public AddSportsPack(ITVBundle tvBundle)
{
this._itv = tvBundle;
}
public override string Description()
{
return _itv.Description() + ", with Sports Pack";
}
public override double Cost()
{
return _itv.Cost() + 9.99;
}
}
class Program
{
static void Main(string[] args)
{
BasicPack bp = new BasicPack();
AddMoviePack plan1 = new AddMoviePack(bp);
AddSportsPack plan2 = new AddSportsPack(plan1);
Console.WriteLine("You have choosen plan 2: {0} \nThe cost is: £{1}",
plan2.Description().ToString(), plan2.Cost().ToString());
}
}
}
The output will be looking like:
Use the Decorator pattern when:
- You have an existing component class that may be unavailable for subclassing.
- You want to attach additional state or behaviour to an object dynamically.
- You want to make changes to some objects in a class without affecting others.
- Avoid subclassing because too many classes could result.
Hi,
ReplyDeleteGood Post.
I had a scenario in which I could use ur advise
I have a structure in which My product has Line of Business(LOB) and Features associated with it.
There is a Feature Master and LOB Master and their Mapping Table LOBFeature
Lobs have feature associated with it that describe how pages are supposed look.
Like a feature can be panel or button so show the resp. on the screen.
There can be situations in which we would need to show a Button witin a panel. So we are currently creating a feature PanelWithButton, I wanted to apply the decorator pattern on this, can you help me how the associations are going to be in the db?