你能删除一个装饰者吗?

是否可以从对象中删除装饰器?

说我有以下代码:

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()); } 

有没有办法从我的新“咖啡”对象中删除“新摩卡()”?

编辑:澄清 – 我希望能够删除一个装饰,而不是所有。 所以如果我在Coffee对象上有一个Mocha装饰器和Sugar装饰器,我想知道我是否可以删除“Mocha”装饰器。

首先,这项任务不合法:

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

Mocha不是Coffee也不是从MochaCoffee的隐含演员。 要“删除”装饰器,您需要提供方法或强制转换来执行此操作。 所以你可以在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 

鉴于此,你需要做什么来解开它?

为了巩固和澄清John K.所说的,Decorator Pattern可以被认为是一个链表 – 在这一点上添加一个setter是很自然的。

要删除图层,只需将其父链接的引用指向其子链接; 或者,在Decorator Pattern术语中,删除装饰器foo,指向foo的装饰器对foo的装饰对象的装饰对象引用。

使用对子项和父项的引用有时可能会让人不知所措。

另一种方法是实现一个抽象装饰器类,其中布尔状态将告诉您装饰器是否必须被视为“开”或“关”。 使用抽象类将允许您将删除装饰器的所有逻辑放在一个位置,然后可以在其上构建所有具体装饰器而不用担心删除。

如果这是你要删除的装饰器,那么你将有一个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对象不是太多并且它们短缺,那么它是值得的。

我会通过调用一个替换当前包装对象的方法来解开,我的意思是,如果我有装饰器ABCDE ,则意味着E包裹D包裹C ,包裹B包裹A 因此,通过调用方法并替换包装对象,我们可以删除所需的装饰器,例如,如果我们要删除装饰器C:

 factory.RemoveDecorator(decoratedObj, replaceDecorator) 

因此,装饰对象将在第二个参数中包装对象。 根据我们需要移除的装饰器,我们将多次调用removedecorator方法。 如果我们只想调用它一次,我们就可以在工厂中编写一个方法来查找将被删除的对象。

如果您使用更灵活的代码进行编码,则可以

要删除一个装饰器,请将它们全部取下并重新组装,而不要放弃要删除的装饰器。 为了解开你,你需要能够引用每一个。 添加表示包装装饰的属性,最内层装饰将表示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属性操作装饰,而不仅仅是在构造时。 允许灵活性。 不确定这是多么犹豫不决。 只是在思考。