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.
First, this assignment is not legal:
A
Mocha
is not aCoffee
nor is there an implicit cast from aMocha
to aCoffee
. To "remove" the decorator, you need to provide either a method or a cast to do so. So you could add an undecorate method toMocha
:Then you could say
Alternatively, you could provide an implicit cast operator in the
Mocha
class:Then your line
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 fromCoffee
!Then you can say:
Given this, what do you need to undecorate it for?
Using a reference to the child and parent Item can be overwhelming sometime.
Another way to go is to implement an abstract decorator class where a boolean state will tell you if the decorator has to be considered "on" or "off". Using an abstract class will allow you to put all the logic for the remove decorator in one place, then all the concrete decorators can be build on top of that without worrying about the remove.
You will have have a remove decorator method that set to true this variable if this is the decorator that you want to remove, doesn't matter the position of the decorator in the chain of decorations:
You are not really removing the decorator but you are neutralizing its effect, it's not the more efficient in term of memory, but definitely will allow you to remove any decorator, how many you want and with only few lines of code. If the Item object are not too many and they live short it can be worthy.
To build on and clarify what John K. said, Decorator Pattern can be thought of as a Linked List-at which point adding a setter is only natural.
To remove a layer, simply point its parent-link's reference at its child-link; or, in Decorator Pattern terms, to remove decorator foo, point foo's decorator's decorated-object reference to foo's decorated-object.
I would undecorate by calling a method to replace the current wrapped object, I mean, if I have the decorators
A
,B
,C
,D
,E
, it means thatE
wrapsD
which wrapsC
which wrapsB
which wrapsA
. So, by calling a method and replacing the wrapped object, we can remove a desired decorator, e.g. if we want to remove the decorator C:So, the decorated object will wrap the object in the second parameter. depending on the decorator we need to remove we will call the method
removedecorator
several times. If we want to call it just once we can write a method in our factory to find which object will be removed.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.
then
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.