This thread, Decorator pattern implementation, has an implementation of a decorator using abstract classes. I don't like it for the simple fact that a CondimentDecorator is NOT a Beverage in the implementation given there. I'd use interfaces instead. Aren't abstract classes better for is-a relationships, and interfaces better for has-a relationships?
public interface IBeverage
{
// get a description of the beverage
String Description { get; }
// calculate cost of the beverage
double Cost { get; }
}
// HouseBlend coffee implements IBeverage
public class HouseBlend : IBeverage
{
private string description;
public String Description
{
get { return description; }
}
private double cost;
public double Cost
{
get { return cost; }
}
// Constructor
public HouseBlend() { description = "House Blend"; cost = 0.89; }
}
// DarkRoast coffee implements IBeverage
public class DarkRoast : IBeverage
{
private string description;
public String Description
{
get { return description; }
}
private double cost;
public double Cost
{
get { return cost; }
}
// Constructor
public DarkRoast() { description = "Dark Roast"; cost = 1.10; }
}
// Mocha is a Decorator
public class Mocha
{
// Mocha has-a Beverage
private IBeverage m_beverage;
private string description;
public String Description
{
get { return description; }
}
private public double Cost
{
get { return cost; }
}
// Constructor binds the object passed to member var
public Mocha(IBeverage beverage)
{
m_beverage = beverage; // not necessary for the purpose of this example
description = m_beverage.Description + ", Mocha";
cost = 0.20 + m_beverage.Cost;
}
}
Use like this:
Mocha mhb = new Mocha(new HouseBlend()); // house blend with mocha flavor
Both base clases and interfaces are frequently used to model is-a relationships. Even an interface as simple as IDisposable
can be understood as "is an object with a manually controlled lifecycle", a "disposable". The more tangible differences are in whether base implementation or data fields are allowed; and in their ability to combine multiple hierarchies.
Now, when you are implementing any pattern, you usually have enough information to see whether you need data fields or not. However, you will hardly ever be able to rule out a future need to involve the same classes in additional patterns as your software grows. From that perspective, a general preference of interfaces over abstract classes gives you more long term flexibility - whenever you have a choice.
By the nature of a Decorator, you have a choice. The components typically do not have a predefined order of how they should nest. If they did, you would use inheritance directly, instead of components. So you should prefer interfaces to compose a Decorator.
All that said, your original argument is valid as well. Decorator components (features) can be understood as is-a relationships if you like; but it is not the most natural way of looking at them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With