Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The Decorator Design Pattern: What is meant by adding functionality?

I have just gone into studying the Decorator Design Pattern using C#.

I have made an example which kinda functions as I think it is intended except for one thing.

I kinda understood that the whole point of the pattern is to add functionality to an object dynamically.

So when I create an object like this:

Inventory a = new Ashbringer(new TravelersBagpack());
a.Execute();

Then I kinda expected that the point was that the a object would now be able to call the Execute() method which only exists in the Ashbringer class. Thus adding functionality to the a object.

Though this I can't do without adding the Execute() method to the Inventory interface which would ultimately mean I would have to implement the Execute() method to all the classes implementing Inventory Interface or the abstract Decorator.

Perhaps there is a thing or two I don't know about interfaces or perhaps I misunderstand the point of the Decorator Design Pattern?

like image 535
Nulle Avatar asked Jun 29 '26 21:06

Nulle


2 Answers

The Decorator pattern is more about changing the behavior of existing methods of an object that you're decorating (wrapping) rather than adding new methods. While the decorator can certainly have its own methods, the intent is that the decorator can be treated as if it were the object it is wrapping.

Consider the Stream classes in .NET. Stream is a common abstract class that simply defines a basic interface for reading and writing to streams, but a concrete subclass like FileStream provides the implementation to read/write files. But if you want to add buffering or compression behavior to a FileStream class, you wouldn't want to subclass FileStream, because then your subclass wouldn't work with NetworkStream or MemoryStream unless you also made subclasses for them.

That's where the Decorator pattern comes in. A BufferedStream or CompressedStream class can "decorate" another instance of a Stream. You can read/write from BufferedStream or CompressedStream the same as you would read/write against any undecorated stream. It does this by overriding the methods of the Stream class, adding its functionality by doing whatever to the bytes coming into or out of the stream, then passing them onto the wrapped object, whether it's a FileStream, NetworkStream, or even another decorator.

But going back to your WoW example, maybe a better example would be an EnchantedWeapon class or a TransmogrifiedWeapon class that would act as decorators of a Weapon class. Then you could do something like:

Weapon w = new Ashbringer();
w = new TransmogrifiedWeapon(w, "Exodar Bastard Sword");
w = new EnchantedWeapon(w, "Scourgebane");
w.Render();

Of course, that would obviously throw an exception, because everybody knows that Ashbringer can't be transmogged. :)

like image 176
Josh Avatar answered Jul 02 '26 09:07

Josh


The idea in the decorator pattern is that you extend the functionality of a method. It is for cases when inheritance becomes to complex and the functionality needs to be in seperated classes.

A good example is calculating prices. Imagine you want to add fees for special services like a nicer package, faster shipping, insurance.

Now you have your Product class with a CalculatePrice method.

public class Product
{
    public double CalculatePrice()
    {
        // 10 is the products price
        return 10.0D;
    }
}

As next you can create a ProductDecorator which doesn't call his base classes method but the one of the product it was given.

public abstract class ProductDecorator : Product
{
    private readonly Product _product;

    public ProductDecorator(Product product)
    {
        _product = product;
    }

    public override double CalculatePrice()
    {
        return _product.CalculatePrice();
    }
}

Now this price has to change when you want extra services like the metioned above. So you can create decorator classes for them.

public class ShippingDecorator : ProductDecorator
{
    public ShippingDecorator(Product product)
        : base(product)
    { }

    public override double CalculatePrice()
    {
        // shipping coasts 5
        return base.CalculatePrice() + 5;
    }
}

public class InsuranceDecorator : ProductDecorator
{
...

If you wanted to use inheritance for this it would not be usefull as it would be to complex. (To many classes)

Now you can use it like this.

var product = new Product();
product = new ShippingDecorator(product);
product = new InsuranceDecorator(product);
...

Typicly you also have a factory which creates your object or you can use the builder patten.

like image 40
NtFreX Avatar answered Jul 02 '26 10:07

NtFreX



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!