Вы можете удалить декоратор? - PullRequest
1 голос
/ 30 января 2010

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

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

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 ()" из моего нового объекта "кофе"?

РЕДАКТИРОВАТЬ: Уточнение - я хочу иметь возможность удалить только ОДИН декоратор, а не все. Поэтому, если бы у меня был объект-декоратор Mocha И декоратор Sugar на объекте Coffee, я хочу знать, смогу ли я удалить только декоратор Mocha.

Ответы [ 5 ]

3 голосов
/ 13 июля 2012

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

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

3 голосов
/ 30 января 2010

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

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

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

Учитывая это, для чего вам нужен декор?

1 голос
/ 24 июля 2012

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

factory.RemoveDecorator(decoratedObj, replaceDecorator)

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

1 голос
/ 01 августа 2011

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

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

У вас будет метод удаления декоратора, который устанавливает значение 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();
        }

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

0 голосов
/ 30 января 2010

Если вы закодируете его с большей гибкостью, вы можете

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

...