时间:2010-01-30 02:45:32

标签: c# design-patterns oop decorator

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

说我有以下代码:

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”装饰器。

5 个答案:

答案 0 :(得分:3)

首先,这项任务不合法:

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

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

答案 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)

我会通过调用替换当前包装对象的方法来解开,我的意思是,如果我有装饰器ABCD,{ {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属性操作装饰,而不仅仅是在构造时。允许灵活性。不确定这是多么犹豫不决。只是在思考。