观察者模式与装饰器模式一起使用

发布于 2024-10-11 19:35:27 字数 1096 浏览 4 评论 0原文

我想制作一个程序来执行饮料订单输入系统。 (我可能会做描述,成本)

我想使用装饰者模式和观察者模式。 我做了一个UML图并将其保存为图片以便于查看。这个网站不允许我作为 Word 文档上传,所以我必须上传一张图片 - 我希望它很容易查看......

在继续编码部分之前,我需要知道我是否正确地执行了 UML/设计模式。

Beverage 是我的抽象组件类。 浓缩咖啡、家庭混合咖啡、深色烘焙是我的具体科目。

我还有调味品装饰师课程 牛奶、摩卡、大豆、鞭子。会是我的观察者吗?因为他们会对数据的成本变化感兴趣?

现在,浓缩咖啡、自制混合咖啡等会成为我的主题,而调味品会成为我的观察者吗? 我的理论是,成本是一个变化,调味品需要知道这些变化?

所以,主题 = esspresso、houseblend、darkroast 等.. // 他们持有 cost()

观察者 = 牛奶、摩卡、大豆、鞭子? // 他们持有 cost()

将是具体组件和 牛奶、摩卡、大豆、鞭子?将是装饰师!

那么,遵循良好的软件工程实践“设计接口而不是实现”或“识别那些不改变的东西”

我是否需要一个成本行为接口?

如果您查看 UML,您将看到我将要处理的内容,并查看我是否正确实现了观察者 + 装饰器模式?我认为装饰者是正确的。


因为图片不太明显,所以我将在这里详细介绍这些类:

饮料类(注册观察者,删除观察者,通知观察者,描述)

这些类是具体的饮料类浓缩

咖啡,houseblend,darkroast,decaf(成本,getdescription,setcost, costchanged)

接口观察者类(update) // 成本?

接口 costbehavior class(cost) // 因为这改变了?

调味品装饰器类(getdescription)

链接到2个接口和装饰器的具体类是: 牛奶、摩卡、大豆、鞭子(成本、获取描述、更新) 这些是我的装饰器/包装器类。

谢谢..

alt text

有没有办法让这张图片变大?

I want to make a program that does an order entry system for beverages. ( i will probably do description, cost)

I want to use the Decorator pattern and the observer pattern.
I made a UML drawing and saved it as a pic for easy viewing. This site wont let me upload as a word doc so i have to upload a pic - i hope its easily viewable....

I need to know if i am doing the UML / design patterns correctly before moving on to the coding part.

Beverage is my abstract component class.
Espresso, houseblend, darkroast are my concrete subject classes..

I also have a condiment decorator class
milk,mocha,soy,whip. would be my observer? because they would be interested in data changes to cost?

Now, would the espresso,houseblend etc, be my SUBJECT and the condiments be my observer?
My theory is that Cost is a changes and that the condiments need to know the changes?

So, subject = esspresso,houseblend,darkroast,etc.. // they hold cost()

Observer = milk,mocha,soy,whip? // they hold cost()

would be the concrete components and the
milk,mocha,soy,whip? would be the decorator!

So, following good software engineering practices "design to an interface and not implementation" or "identify things that change from those that dont"

would i need a costbehavior interface?

If you look at the UML you will see where i am going with this and see if i am implementing observer + Decorator pattern correctly? I think the decorator is correct.


since, the pic is not very viewable i will detail the classes here:

Beverage class(register observer, remove observer, notify observer, description)

these classes are the concrete beverage classes

espresso, houseblend,darkroast, decaf(cost,getdescription,setcost,costchanged)

interface observer class(update) // cost?

interface costbehavior class(cost) // since this changes?

condiment decorator class( getdescription)

concrete classes that are linked to the 2 interface s and decorator are:
milk,mocha,soy,whip(cost,getdescription,update)
these are my decorator/ wrapper classes.

Thank you..

alt text

Is there a way to make this picture bigger?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

平定天下 2024-10-18 19:35:27

我可以看到装饰器在这里发挥作用,但我不太确定在这里使用观察者。看来是被迫的。

有几件事:

  1. CondimentDecorator 需要有饮料来装饰。它不会显示在您的 UML 中。将其视为某种事物的包装,类似于适配器模式。包装纸需要一些东西来包裹。你显然没有表现出这一点。
  2. 在你的成本行为界面上,为什么具体的装饰器会实现它,而具体的饮料却不会,而饮料也有成本呢?我只是让饮料接口继承自成本行为接口并完成它。
  3. #2 也适用于 getDescription 方法...创建 IDescription?或其他东西并让饮料实现该功能。装饰器会通过继承来获取它。
  4. 你需要说服我为什么调味品需要知道价格何时变化?目前,这对我来说似乎有点被迫。我不明白这里如何需要观察者(逻辑上/设计方面)。在我看来,你想要将观察者模式强加到这个设计中,除了你必须拥有它之外,没有什么充分的理由。对于刚刚学习设计模式的人来说,这是一个经典的模式热衷事物。无意冒犯,只是陈述事实。我曾经也是这样的人:)。

我的建议是再次阅读《Head First Design Pattern》一书(我想你可以从这本书中获得这个示例:),非常相似的领域),并更好地理解这两种模式以及何时使用它们。

下面是装饰器和观察者一起工作的示例。饮料组合是装饰器的实现。订购系统是一个观察者(订单将通知寻呼机,寻呼机将执行某些操作)。这里的场景是一家星巴克咖啡店,他们会给你一个寻呼机,这样你就可以在处理饮料时四处走动,一旦饮料准备好,你就会收到寻呼机的通知。

将示例保存为 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();
  }
}

I can see decorator comes into play here, but I am not too sure about using observer here. It seems forced.

Several things:

  1. CondimentDecorator need to a have a beverage to decorate. It doesn't show in your UML. Think of it as a wrapper around something, similar to Adapter pattern. A wrapper need something to wrap on. You clearly didn't show that.
  2. On your cost behavior interface, why would the concrete decorators implemented it, but the concrete beverages do not, and yet, the beverage has cost also in it? I'd just have beverage interface inherit from cost behavior interface and be done with it.
  3. #2 also applies to the getDescription method... create an IDescribable? or something and have beverage implement that. Decorator will get it through inheritance.
  4. You need to convince me why would a condiment need to know when the price change? At the moment, this seems a bit forced to me. I don't see how observer is needed here (logically / design-wise). It seems to me that you want to force an observer pattern into this design for no good reason other than you must have it. This is a classic pattern gung-ho thing for someone that just learn about design pattern. Not trying to be offensive, just stating the fact. I was once such a person as well :).

My suggestion is to read up again on the Head First Design Pattern book (which I think where you get this examples :), very similar domain) and get a better understanding on both patterns and when to use them.

Below is an example of decorator and observer working together. The drink combination is an implementation of decorator. The ordering system is an observer (where the order will notify the pager and the pager will do something). The scenario here is a StarBuck coffee shop where they'll give you a pager so you can go around doing something while your drink is being processed and you will get notified by the pager once the drink is ready.

Save the sample as drink.cs and you can easily compile this using csc ( C:\Windows\Microsoft.Net\Framework\v3....\csc /target:exe /out:drink.exe drink.cs) and run it or use VS or whatever :).

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();
  }
}
北座城市 2024-10-18 19:35:27

我绝对不是设计模式专家,但我觉得你让这个变得比需要的更复杂。另外,我不是 UML 专家,但是您是否将每种咖啡类型作为单独的类?我认为,拥有一个具有类型属性/属性的饮料类,然后在其中注入一个“成本细节”类,这将允许这些饮料报告正确的价格,这样会更有意义。

I am definitely not a design pattern expert but I feel here that you are making this more complex than it needs to be. Also, I'm no UML expert but is it that you have each of your coffee types as a separate class? I think it would make more sense to have a beverage class with a type property/attribute and then to inject a 'cost-detail' class into this which will allow these beverages to report correct prices.

软甜啾 2024-10-18 19:35:27

不,这根本不对。抱歉,但事实并非如此。该图画表明一切都是饮料,在某些情况下甚至多次如此。显然,其中一些白色三角形应该是菱形,其中大多数可能是黑色的。您使用的唯一关系是继承,这显然不是您的意图。

我对设计理念也有严重的问题,但我想这是另一个讨论的问题。对于刚开始设计的人来说,你所做的一切都没有什么不同寻常的(每个人一开始都严重过度设计每一件该死的东西),所以我不会对此感到难过。至少你在考虑设计,这是一件非常好的事情。


回复评论:

没什么,用在需要的地方。当它应用到不应用的地方时,就相当多了。这里根本不需要它。此外,你会做错事。奶油不是咖啡的装饰器(我认为这就是你想要做的)加奶油的咖啡可以......但这仍然是过度设计。

装饰器为其所装饰的事物添加行为。假设您有一个消息类和一个显示输出的打印函数:

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

看看它如何改变任何具体消息的行为,而不必更改原始消息?

你的咖啡/调味品不是这样的。调味品就是你放入咖啡中的东西。例如,一杯加或不加奶油的咖啡在行为上没有区别。如果您想对添加的奶油收费,那么您只需将其与咖啡杯相关联,并在询问杯子的价格时计算总计。

No. It's not right at all. Sorry, but it isn't. That drawing says that EVERYTHING is a beverage, in some cases many times over. Clearly some of those white triangles should be diamonds, most of them probably black. The only relationship you've used is inheritance and it's obviously not your intention.

I also have serious problems with the design idea, period, but I guess that's for another discussion. Nothing you've done is out of ordinary for someone just starting with design (everyone starts out seriously overdesinging every damn thing), so I wouldn't feel bad about it though. At least you're thinking about design, and that's a pretty darn good thing.


Reply to comment:

Nothing, when applied where it's needed. Quite a bit when it's applied where it's not. It's simply not needed here. Furthermore, you'd be doing it wrong. Cream is not a decorator for coffee (I think that's what you're trying to do) Coffee with cream could be...but that's still overdesign.

A decorator adds behavior to the thing it's decorating. Say you had a message class and a print function that displayed the output:

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

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

Now lets say you wanted to have some of them indented. You could do that by using a "decorator", which subclasses AND contains a message:

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

See how that changes the behavior of any concrete message without having to change the original?

What you've got with your coffee/condiment thing isn't like this. Condiments are just things you put in coffee. There's no difference in behavior between a cup of coffee with or without cream (for example). If you want to charge for cream added then you simply associate it with the cup of coffee and run a total when you ask the cup how much it is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文