views:

139

answers:

2

Is it possible to remove a decorator from an object?

Say I have the following code:

abstract class Item
{
    decimal cost();
}

class Coffee : Item
{
    decimal cost()
    { // some stuff }
}

abstract class CoffeeDecorator : Item
{
    Item decoratedItem;
}

class Mocha : CoffeeDecorator 
{
    Item decoratedItem;

    public Mocha(Coffee coffee)
    {
       decoratedItem = coffee;
    }
}

public void Main(string[] args)
{
    Item coffeeDrink = new Mocha(new Coffee());
}

Is there a way to remove the "new Mocha()" from my new "coffee" object?

EDIT: Clarification - I want to be able to remove just ONE decorator, not all of them. So if I had a Mocha decorator AND a Sugar decorator on the Coffee object, I want to know if I can remove just the "Mocha" decorator.

+2  A: 

First, this assignment is not legal:

Coffee coffee = new Mocha(new Coffee());

A Mocha is not a Coffee nor is there an implicit cast from a Mocha to a Coffee. To "remove" the decorator, you need to provide either a method or a cast to do so. So you could add an undecorate method to Mocha:

public Coffee Undecorate() {
    return (Coffee)decoratedItem;
}

Then you could say

Coffee coffee = new Mocha(new Coffee()).Undecorate();

Alternatively, you could provide an implicit cast operator in the Mocha class:

public static implicit operator Coffee(Mocha m) {
    return (Coffee)m.decoratedItem;
}

Then your line

Coffee coffee = new Mocha(new Coffee());

would be legal.

Now, your question suggests a potential misunderstanding of the design pattern (and, in fact, your implementation suggests one too). What you're trying to do is very smelly. The right way to go about using the decorator pattern is like so. Note that CoffeeDecorator derives from Coffee!

abstract class Item { public abstract decimal Cost(); }
class Coffee : Item { public override decimal Cost() { return 1.99m; } }
abstract class CoffeeDecorator : Coffee {
    protected Coffee _coffee;
    public CoffeeDecorator(Coffee coffee) { this._coffee = coffee; }
}
class Mocha : CoffeeDecorator {
    public Mocha(Coffee coffee) : base(coffee) { }
    public override decimal Cost() { return _coffee.Cost() + 2.79m; }
}
class CoffeeWithSugar : CoffeeDecorator {
    public CoffeeWithSugar(Coffee coffee) : base(coffee) { }
    public override decimal Cost() { return _coffee.Cost() + 0.50m; }
}

Then you can say:

Coffee coffee = new Mocha(new CoffeeWithSugar(new Coffee()));
Console.WriteLine(coffee.Cost()); // output: 5.28

Given this, what do you need to undecorate it for?

Jason
sorry let me fix that
mikedev
I should have clarified - I want to remove just one decorator, for example if I had 3 different decorators I want to end up with just 2. You solution looks like it's removing all decorators.
mikedev
Say that you had `DecoratedCoffee coffee = new Mocha(new CoffeeWithSugar(new Coffee()));` and you had a method `DecoratedCoffee.Undecorate`. What should be the return type of `DecoratedCoffee.Undecorate`? If it's `DecoratedCoffee` then what's the result of `new CoffeeWithSugar(new Coffee()).Undecorate()`? It should be the new instance of `Coffee` but it can't be because it's return type is `DecoratedCoffee`; `DecoratedCoffee` as the return type is absurd. So, it should be `Coffee` But then `new Mocha(new CoffeeWithSugar(new Coffee())).Undecorate().Undecorate()` is not possible without casting.
Jason
But this suggests that there's something smelly about the way that you're trying to go about things.
Jason
+1 for stating the code is smelly.
JonH
thanks for the insight on this. for the record, i'm using the decorator for an rpg game i'm programming (for fun and to learn). i was going to decorate my "Character" class with say "Mage" and "Theif" and "Warrior" so a character could multi-class. and i wanted the ability to remove one class if necessary. but maybe i'm going about this the wrong way.
mikedev
A: 

If you code it with more flexiblity you can

To remove one decorator, unpeel them all and reassemble without the one you want to leave out. To unpeel you'll need to be able to reference each one. Add a property that expresses the wrapped decoration and the innermost decoration will express null.

interface IDecoratedExpressing {
    IDecoratedExpressing InnerDecorated {get;}
}

then

// NOTE: implement IDecoratedExpressing for all decorations to provide a handle. 

// Example of first:

class Mocha : CoffeeDecorator, IDecoratedExpressing 
{
    Item decoratedItem;

    // express inner
    public IDecoratedExpressing InnerDecorated {
        get {return decoratedItem;}
    }

    public Mocha(Coffee coffee)
    {
       decoratedItem = coffee;
    }

}

Maybe make the InnerDecorated property settable too, so you can put them back together in a different way (or to leave one or more out). This means you can manipulate a decoration through a setter property instead of just at construction time. Allows flexibility. Unsure how kosher this is. Just thinking on the fly.

John K