是否可以从对象中删除装饰器?
说我有以下代码:
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());
}
有没有办法从我的新“咖啡”对象中删除“新Mocha()”?
编辑:澄清 - 我希望能够删除一个装饰器,而不是所有装饰器。所以,如果我在Coffee对象上有一个Mocha装饰器和Sugar装饰器,我想知道我是否可以删除“Mocha”装饰器。
答案 0 :(得分:3)
首先,这项任务不合法:
Coffee coffee = new Mocha(new Coffee());
Mocha
不是Coffee
,也不是从Mocha
到Coffee
的隐式演员。要“移除”装饰器,您需要提供方法或强制转换来执行此操作。因此,您可以向Mocha
添加undecorate方法:
public Coffee Undecorate() {
return (Coffee)decoratedItem;
}
然后你可以说
Coffee coffee = new Mocha(new Coffee()).Undecorate();
或者,您可以在Mocha
类中提供隐式强制转换运算符:
public static implicit operator Coffee(Mocha m) {
return (Coffee)m.decoratedItem;
}
然后你的行
Coffee coffee = new Mocha(new Coffee());
是合法的。
现在,你的问题暗示了对设计模式的潜在误解(事实上,你的实现也表明了这一点)。你要做的是非常臭。使用装饰器模式的正确方法就是这样。请注意CoffeeDecorator
来自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; }
}
然后你可以说:
Coffee coffee = new Mocha(new CoffeeWithSugar(new Coffee()));
Console.WriteLine(coffee.Cost()); // output: 5.28
鉴于此,你需要做什么来解开它?
答案 1 :(得分:3)
为了巩固和澄清John K.所说的,Decorator Pattern可以被认为是一个链表 - 在这一点上添加一个setter是很自然的。
要删除图层,只需将其父链接的引用指向其子链接即可;或者,在Decorator Pattern术语中,删除装饰器foo,指向foo的装饰器对foo的装饰对象的装饰对象引用。
答案 2 :(得分:1)
使用对子项和父项的引用可能会在某个时候压倒一切。
另一种方法是实现一个抽象装饰器类,其中布尔状态将告诉您装饰器是否必须被视为“开”或“关”。使用抽象类将允许您将删除装饰器的所有逻辑放在一个位置,然后可以在其上构建所有具体装饰器而不必担心删除。
如果这是你要移除的装饰器,那么你将有一个remove decorator方法设置为true这个变量,与装饰链中装饰器的位置无关:
public void RemoveDecorator(DECORATOR_CODES decCode)
{
if (this.Code == decCode)
{
bDecoratorRemoved = true;
}
else
this.ParentBevarage.RemoveDecorator(decCode);
}
public float Cost()
{
if (!bDecoratorRemoved)
return this.ParentBevarage.Cost() + this.Price;
else
return this.ParentBevarage.Cost();
}
你并没有真正删除装饰器,但是你正在中和它的效果,它在内存方面并不是更有效率,但绝对会允许你删除任何装饰器,你想要多少,只需几行代码。如果Item对象不是太多而且它们很短,那就值得。
答案 3 :(得分:1)
我会通过调用替换当前包装对象的方法来解开,我的意思是,如果我有装饰器A
,B
,C
,D
,{ {1}},这意味着E
包裹E
包裹D
包裹C
的{{1}}包裹B
。因此,通过调用方法并替换包装的对象,我们可以删除所需的装饰器,例如,如果我们想删除装饰器C:
A
因此,装饰对象将把对象包装在第二个参数中。根据我们需要移除的装饰器,我们将多次调用方法factory.RemoveDecorator(decoratedObj, replaceDecorator)
。如果我们只想调用它一次,我们可以在工厂中编写一个方法来查找将被删除的对象。
答案 4 :(得分:0)
如果您使用更灵活的代码进行编码,则可以
要移除一个装饰器,请将它们全部取下并重新组装,而不要丢弃。为了解开你,你需要能够引用每一个。添加一个表达包装装饰的属性,最里面的装饰将表示null。
interface IDecoratedExpressing {
IDecoratedExpressing InnerDecorated {get;}
}
然后
// 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;
}
}
也许也可以设置InnerDecorated
属性,这样你就可以以不同的方式将它们重新组合在一起(或者将一个或多个放在一起)。这意味着您可以通过setter属性操作装饰,而不仅仅是在构造时。允许灵活性。不确定这是多么犹豫不决。只是在思考。