【问题标题】:Can you remove a decorator?你能去掉一个装饰器吗?
【发布时间】:2011-01-11 02:50:31
【问题描述】:

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

假设我有以下代码:

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

【问题讨论】:

    标签: c# design-patterns oop decorator


    【解决方案1】:

    我会通过调用一个方法来替换当前包装的对象来取消装饰,我的意思是,如果我有装饰器A,B,C,D,E,这意味着@987654326 @包裹D包裹C包裹B包裹A。因此,通过调用一个方法并替换被包装的对象,我们可以移除一个想要的装饰器,例如如果我们要移除装饰器 C:

    factory.RemoveDecorator(decoratedObj, replaceDecorator)
    

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

    【讨论】:

      【解决方案2】:

      为了建立和阐明 John K. 所说的,装饰器模式可以被认为是一个链接列表——此时添加一个 setter 是很自然的。

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

      【讨论】:

        【解决方案3】:

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

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

        如果这是您要删除的装饰器,您将有一个 remove 装饰器方法将这个变量设置为 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 对象不是太多并且它们的寿命很短,那么它是值得的。

        【讨论】:

          【解决方案4】:

          如果你可以更灵活地编写代码

          要移除一个装饰器,请将它们全部剥去,然后重新组装,不要留下你想要的装饰器。要取消剥离,您需要能够参考每一个。添加一个表示包裹装饰的属性,最里面的装饰将表示为 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 属性来操作装饰,而不仅仅是在构建时。允许灵活性。不确定这是多么洁净。只是在飞行中思考。

          【讨论】:

            【解决方案5】:

            首先,这个赋值是不合法的:

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

            Mocha 不是Coffee,也没有从MochaCoffee 的隐式转换。要“删除”装饰器,您需要提供方法或强制转换来执行此操作。所以你可以给Mocha添加一个不修饰的方法:

            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
            

            鉴于此,你需要取消装饰它做什么?

            【讨论】:

            • 我应该澄清一下 - 我只想删除一个装饰器,例如,如果我有 3 个不同的装饰器,我想最终只删除 2 个。您的解决方案看起来像是删除了所有装饰器。
            • 假设你有DecoratedCoffee coffee = new Mocha(new CoffeeWithSugar(new Coffee()));,你有一个方法DecoratedCoffee.UndecorateDecoratedCoffee.Undecorate 的返回类型应该是什么?如果是DecoratedCoffee,那么new CoffeeWithSugar(new Coffee()).Undecorate() 的结果是什么?它应该是Coffee 的新实例,但不可能是因为它的返回类型是DecoratedCoffeeDecoratedCoffee 作为返回类型是荒谬的。所以,它应该是Coffee 但是如果没有强制转换,new Mocha(new CoffeeWithSugar(new Coffee())).Undecorate().Undecorate() 是不可能的。
            • 但这表明您尝试做事的方式有些异味。
            • 感谢您对此的见解。作为记录,我正在将装饰器用于我正在编程的 rpg 游戏(为了好玩和学习)。我打算用“Mage”、“Theif”和“Warrior”来装饰我的“Character”类,这样一个角色就可以多类。我希望能够在必要时删除一个类。但也许我做错了。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-03
            • 2016-11-07
            • 2020-08-28
            • 1970-01-01
            • 2013-08-24
            • 2012-05-14
            相关资源
            最近更新 更多