Шаблон наблюдателя, используемый с шаблоном декоратора - PullRequest
3 голосов
/ 15 января 2011

Я хочу сделать программу, которая делает систему ввода заказов на напитки.(я, вероятно, сделаю описание, стоимость)

Я хочу использовать шаблон Decorator и шаблон наблюдателя.Я сделал рисунок UML и сохранил его в виде картинки для удобного просмотра.Этот сайт не позволяет мне загружать как документ Word, поэтому мне нужно загрузить фотографию - я надеюсь, что ее легко просмотреть ....

Мне нужно знать, правильно ли я выполняю шаблоны UML / дизайна, прежде чем двигаться дальше.к части кода.

Напиток - мой абстрактный класс компонента.Эспрессо, houseblend, darkroast - мои конкретные предметные классы ..

У меня также есть класс декораторов приправы молоко, мокко, соя, кнут.будет моим наблюдателем?потому что они были бы заинтересованы в изменении данных к стоимости?

Теперь, будет ли эспрессо, домашняя посуда и т. д. моим СУБЪЕКТОМ, а приправы - моим наблюдателем?Моя теория состоит в том, что Стоимость - это изменения, и что приправы должны знать об изменениях?

Итак, subject = esspresso, houseblend, darkroast и т. Д. // они содержат стоимость ()

Наблюдатель = молоко, мокко, соя, кнут?// они содержат стоимость ()

Будут ли конкретные компоненты и молоко, мокко, соя, кнут?был бы декоратором!

Итак, следуя хорошим методам разработки программного обеспечения «дизайн для интерфейса, а не реализация» или «выявлять вещи, которые отличаются от тех, которые не делают»,

мне бы понадобился интерфейс с поведением затрат?

Если вы посмотрите на UML, то увидите, куда я иду с этим, и увидите, правильно ли я реализую шаблон наблюдатель + декоратор?Я думаю, что декоратор правильный.


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

Класс напитка (зарегистрировать наблюдателя, удалить наблюдателя, уведомить наблюдателя, описание)

эти классы относятся к классам конкретных напитков

эспрессо, домашний напиток, темный жар, без кофеина (стоимость, getdescription, setcost, стоимостный обмен)

класс интерфейса наблюдателя (обновление) //стоимость?

интерфейс класса поведения класса (стоимость) // как это меняется?

класс декоратора приправ (getdescription)

конкретные классы, которые связаны с 2 интерфейсами и декораторомявляются: молоко, мокко, соя, кнут (стоимость, описание, обновление) Это мои классы декоратор / упаковщик.

Спасибо ..

alt text

ЕстьЕсть ли способ сделать эту картинку больше?

Ответы [ 3 ]

1 голос
/ 15 января 2011

Я вижу, что декоратор вступает в игру здесь, но я не слишком уверен в использовании наблюдателя здесь. Кажется вынужденным.

Несколько вещей:

  1. CondimentDecorator нужно выпить, чтобы украсить. Это не отображается в вашем UML. Думайте об этом как обертка вокруг чего-то, похожего на шаблон адаптера. Обертке нужно что-то намотать. Вы явно не показали это.
  2. На вашем интерфейсе поведения с ценой, почему бетонные декораторы реализовали его, а конкретные напитки - нет, и тем не менее, напиток также имеет стоимость? Мне бы просто нужно было наследовать интерфейс напитка от интерфейса поведения затрат и покончить с этим.
  3. # 2 также относится к методу getDescription ... создать IDescribable? или что-то и есть напиток реализовать это. Декоратор получит его по наследству.
  4. Вы должны убедить меня, почему приправа должна знать, когда цена изменится? На данный момент это кажется немного вынужденным. Я не вижу, как здесь нужен наблюдатель (логически / по дизайну). Мне кажется, что вы хотите ввязать шаблон наблюдателя в этот проект только по той причине, что вы должны его иметь. Это классическая модель gung-ho вещь для тех, кто только что узнал о дизайне шаблона. Не пытаясь обидеть, просто констатирую факт. Я тоже когда-то был таким человеком:).

Мое предложение состоит в том, чтобы снова прочитать книгу Head First Design Pattern (которая, я думаю, где вы взяли эти примеры :), очень похожую область) и получить лучшее понимание обоих шаблонов и того, когда их использовать.

Ниже приведен пример совместной работы декоратора и наблюдателя. Комбинация напитков - это реализация декоратора. Система заказа является наблюдателем (где заказ уведомит пейджер, а пейджер что-то сделает). Сценарий здесь - это кафе StarBuck, где вам дадут пейджер, чтобы вы могли что-то делать во время обработки вашего напитка, и вы получите уведомление от пейджера, когда напиток будет готов.

Сохраните образец как drink.cs, и вы можете легко скомпилировать его, используя csc (C: \ Windows \ Microsoft.Net \ Framework \ v3 .... \ csc / target: exe /out:drink.exe drink.cs ) и запустите его или используйте VS или что-то еще:).

using System;
using System.Collections.Generic;

public interface IBeverage
{
  string GetDescription();
  decimal GetCost();
}

public abstract class Beverage : IBeverage
{
  protected string _name;
  protected decimal _cost;

  public Beverage(string name, decimal cost)
  {
     _name = name;
     _cost = cost;
  }

  public virtual string GetDescription()
  {
    return _name;
  }

  public virtual decimal GetCost()
  {
    return _cost;
  }
}

public class Macchiato : Beverage
{
  public Macchiato() : base("Macchiato", 3.50m) {}
}

public abstract class BeverageDecorator : Beverage
{
  IBeverage _baseBeverage;

  public BeverageDecorator(IBeverage baseBeverage) : base("", 0m)
  {
    _baseBeverage = baseBeverage;
  }

  public override string GetDescription()
  {
    return _name + " " + _baseBeverage.GetDescription();
  }

  public override decimal GetCost()
  {
    return _cost + _baseBeverage.GetCost();
  }
}

public class Caramel : BeverageDecorator
{
  public Caramel(IBeverage baseBeverage) : base(baseBeverage) 
  {
     _name = "Caramel";
     _cost = 0.50m;
  }
}

public class Venti : BeverageDecorator
{
  public Venti(IBeverage baseBeverage) : base(baseBeverage)
  {
     _name = "Venti";
     _cost = 1.00m;
  }
}

public class Iced : BeverageDecorator
{
  public Iced(IBeverage baseBeverage) : base(baseBeverage)
  {
    _name = "Iced";
    _cost = 0.25m;
  }
}

public class Order
{
  IBeverage _beverage;
  IPager _pager;

  public Order(IBeverage beverage, IPager pager)
  {
    _beverage = beverage;
    _pager = pager;
  }

  public IPager Pager
  {
    get { return _pager; }
  }

  public IBeverage Beverage
  {
    get { return _beverage; }
  }
}

public class OrderProcessing
{
    Queue<Order> orders = new Queue<Order>();

    public void NewOrder(IBeverage beverage, IPager pager)
    {
      orders.Enqueue(new Order(beverage, pager));
    }

    public void ProcessOrder()
    {
      if (orders.Count > 0)
      {
        var order = orders.Dequeue();
        order.Pager.Update(order);
      }
    }
}

public interface IPager
{
  void Update(Order order);
}

public class VibratingPager : IPager
{
  string _number;

  public VibratingPager(string number)
  {
    _number = number;
  }

  public void Update(Order order)
  {
    Console.WriteLine("BUZZZ");
    Console.WriteLine("Your {0} is ready.  Please pay {1} at the cashier after picking it up.", order.Beverage.GetDescription(),order.Beverage.GetCost());
  }
}

public class Program
{
  public static void Main(string[] args)
  {  
    var orders = new OrderProcessing();
    var pager1 = new VibratingPager("1");
    var pager2 = new VibratingPager("2");    

    orders.NewOrder(new Iced(new Venti(new Caramel(new Macchiato()))), pager1);
    orders.NewOrder(new Venti(new Macchiato()), pager2);

    orders.ProcessOrder();
    orders.ProcessOrder();
  }
}
1 голос
/ 15 января 2011

Я определенно не эксперт по шаблонам проектирования, но я чувствую, что вы делаете это более сложным, чем нужно. Кроме того, я не эксперт по UML, но у вас есть каждый тип кофе в отдельном классе? Я думаю, что было бы более разумно иметь класс напитка со свойством / атрибутом типа, а затем добавить в него класс «подробности затрат», который позволит этим напиткам сообщать правильные цены.

0 голосов
/ 15 января 2011

Нет.Это совсем не правильно.Извините, но это не так.На этом рисунке написано, что ВСЕ - это напиток, в некоторых случаях много раз.Очевидно, что некоторые из этих белых треугольников должны быть бриллиантами, большинство из которых, вероятно, черные.Единственные отношения, которые вы использовали, - это наследование, и это явно не ваше намерение.

У меня также есть серьезные проблемы с идеей дизайна, точка, но я думаю, что это для другого обсуждения.Ничто из того, что вы сделали, не является чем-то необычным для кого-то, кто только начинает с дизайна (все начинают серьезно переоценивать каждую чертову вещь), поэтому я не буду расстраиваться из-за этого.По крайней мере, вы думаете о дизайне, и это чертовски хорошо.


Ответ на комментарий:

Ничего, когда применяется там, где это необходимо.Совсем немного, когда он применяется там, где его нет.Это просто не нужно здесь.Кроме того, вы сделали бы это неправильно.Крем не является декоратором для кофе (я думаю, это то, что вы пытаетесь сделать) Кофе со сливками может быть ... но это по-прежнему чрезмерно.

Декоратор добавляет поведение к вещи, которую он украшает.Допустим, у вас есть класс сообщения и функция печати, которая отображает вывод:

struct message
{
  virtual void print() = 0;
};

struct concrete_message : message
{
  void print() { std::cout << my_message << std::endl; }
};

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

struct tabbed_message
{
  tabbed_message(message * msg) : my_message(msg) {}
  void print() { std::cout << "\t; my_message->print(); }
};

Посмотрите, как это меняет поведение любого конкретного сообщения без необходимости изменять оригинал?

То, что у вас есть с кофе / приправой, совсем не так.Приправы - это просто вещи, которые вы кладете в кофе.Нет разницы в поведении между чашкой кофе с кремом или без него (например).Если вы хотите взимать плату за добавленные сливки, вы просто связываете их с чашкой кофе и подсчитываете, сколько это стоит.

...