2010-01-30 2 views
1

Можно ли снять декоратор с объекта?Можете ли вы удалить декоратор?

Скажем, у меня есть следующий код:

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

Есть ли способ, чтобы удалить «новый Мокко()» от моего нового «кофе» объект?

EDIT: Уточнение. Я хочу, чтобы убрать только ОДИН декоратор, не все из них. Поэтому, если бы у меня был декоратор Mocha и декоратор сахара на предмете Coffee, я хочу знать, смогу ли я удалить только декоратор «Мокка».

ответ

3

Во-первых, это назначение не является законным:

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

Mocha не Coffee и не существует неявное приведение из Mocha к Coffee. Чтобы «удалить» декоратор, вам нужно предоставить метод или бросок, чтобы сделать это. Таким образом, вы можете добавить undecorate метод 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 

Учитывая это, то, что вам нужно undecorate это?

+0

Извините, позвольте мне исправить это. – mikedev

+0

Я должен был уточнить. Я хочу удалить только один декоратор, например, если бы у меня было 3 разных декоратора, я хочу закончить всего 2. Решение похоже на удаление всех декораторов. – mikedev

+0

Скажите, что у вас был 'DecoratedCoffee coffee = new Mocha (новый CoffeeWithSugar (новый Coffee())),' и у вас был метод 'DecoratedCoffee.Undecorate'. Каким должен быть тип возврата 'DecoratedCoffee.Undecorate'? Если это 'DecoratedCoffee', то какой результат« нового CoffeeWithSugar (новый кофе()). Undecorate() '? Это должен быть новый экземпляр «Кофе», но это не может быть потому, что его тип возврата - «DecoratedCoffee»; 'DecoratedCoffee', поскольку тип возврата абсурден. Таким образом, это должен быть «Кофе». Но затем «новый мокко» (новый CoffeeWithSugar (новый кофе()). Undecorate(). Undecorate() 'невозможно без кастинга. – jason

0

Если вы закодировать его с более flexiblity вы можете

Чтобы удалить один декоратор, unpeel их всех и собрать без одного вы хотите выйти из. Чтобы отключиться, вы должны иметь возможность ссылаться на них. Добавьте свойство, которое выражает завернутое украшение, а самое внутреннее украшение выражает нуль.

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, а не просто во время строительства. Позволяет гибкость. Не знаете, насколько это кошерный. Просто думать на лету.

1

Использование ссылки на ребенка и родителя. Предмет может быть подавляющим когда-то.

Еще один способ - реализовать абстрактный класс декоратора, в котором булевское состояние скажет вам, следует ли считать декоратор «включенным» или «выключен». Использование абстрактного класса позволит вам разместить всю логику для удаления декоратора в одном месте, тогда все конкретные декораторы могут быть построены поверх этого, не беспокоясь об удалении.

Вы должны иметь способ удалить декоратор, который установлен в истинный этом переменный, если это декоратор, который вы хотите удалить, не имеет значения, положения декоратора в цепи украшений:

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

Чтобы опираться на то, что сказал Джон К., пояснил, что узор декоратора можно рассматривать как связанный список - в этот момент добавление сеттера является естественным.

Чтобы удалить слой, просто укажите его ссылку на родительскую ссылку в его дочерней ссылке; или, в терминах Decorator Pattern, для удаления декоратора foo, ссылки на декорированный объект декоративного объекта point foo на декорированный объект foo.

1

Я бы undecorate путем вызова метода, чтобы заменить текущий обернутый объект, я имею в виду, если у меня есть декораторы A, B, C, D, E, это означает, что E обручи D который оборачивает C который оборачивает B который оборачивает A. Таким образом, вызывая метод и заменяя обернутый объект, мы можем удалить желаемый декоратор, например. если мы хотим, чтобы удалить декоратор C:

factory.RemoveDecorator(decoratedObj, replaceDecorator) 

Так, декорированный объект будет обернуть объект в качестве второго параметра. в зависимости от декоратора, который нужно удалить, мы будем называть метод removedecorator несколько раз. Если мы хотим называть это только один раз, мы можем написать метод на нашей фабрике, чтобы найти, какой объект будет удален.

Смежные вопросы