关于uml:与装饰器模式一起使用的Observer模式

Observer pattern used with decorator pattern

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

我想使用decorator模式和observer模式。我画了一个UML图,并把它保存为一张图片,以便查看。这个网站不允许我作为word文档上传,所以我必须上传一张图片-我希望它很容易被看到…

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

饮料是我的抽象组件类。浓缩咖啡,室内混合咖啡,黑烤肉是我的具体课程。

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

现在,我的主题是浓缩咖啡、家常菜等等,而调味品是我的观察者吗?我的理论是,成本是一个变化,而调味品需要知道变化?

所以,主题=Espresso、HouseBlend、Darkroast等。//他们持有成本()。

观察者=牛奶、摩卡咖啡、大豆、鞭子?//他们持有成本()。

将是混凝土构件和牛奶,摩卡,大豆,鞭子?会是装修工!

因此,遵循良好的软件工程实践"设计到接口而不是实现"或"识别那些从没有改变的事物"

我需要CostBehavior接口吗?

如果您查看UML,您将看到我将如何处理这个问题,并看到我是否正确地实现了observer+decorator模式?我认为装潢师是对的。

由于图片不太清晰,我将在这里详细介绍这些类:

饮料类(登记观察者、删除观察者、通知观察者、描述)

这些类别是具体的饮料类别

浓缩咖啡、室内混合咖啡、黑烤肉、德卡夫咖啡(成本、GetDescription、SetCost、CostChanged)

接口观察器类(更新)//成本?

interface costbehavior class(cost)//此更改之后?

调味品装饰类(getdescription)

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

谢谢您。。

alt text

有没有办法把这张照片放大一点?


我可以看到装饰师在这里起作用,但我不太确定是否要在这里使用观察者。这似乎是被迫的。

几件事:

  • 调味品制冷器需要一个有饮料来装饰。它不会显示在您的UML中。把它看作是围绕某个东西的包装器,类似于适配器模式。包装需要一些东西来包装。你显然没有表现出来。
  • 在您的成本行为界面上,为什么具体的装饰师会实现它,但是具体的饮料没有,然而,饮料中也有成本?我只需要让饮料接口继承自成本行为接口,并完成它。
  • #2也适用于GetDescription方法…创建IDescribable?或者其他东西,并且有饮料器具。装饰师将通过继承获得它。
  • 你需要说服我为什么调味品需要知道什么时候价格变化?目前,这似乎有点强迫我。我不知道这里需要观察者(逻辑上/设计上)。在我看来,你想强制一个观察者模式进入这个设计,除了你必须拥有它之外,没有什么好的理由。这是一个典型的模式,刚学会设计模式的人。不想冒犯别人,只是陈述事实。我曾经也是这样的人。
  • 我的建议是再次阅读head-first设计模式书(我认为你从哪里得到这个例子:),非常相似的领域),并更好地理解这两种模式以及何时使用它们。

    下面是装饰师和观察者一起工作的一个例子。饮料组合是装饰器的一个实现。订购系统是一个观察员(在这里,订单将通知传呼机,传呼机将做一些事情)。这里的场景是一家星巴克咖啡店,他们会给你一个传呼机,这样你就可以在饮料加工过程中四处走动,一旦饮料准备好,传呼机就会通知你。

    将示例另存为drink.cs,您可以使用cs c(c:windowsmicrosoft.netframeworkv3….csc/target:exe/out:drink.exe drink.cs)轻松编译此示例,然后运行它或使用vs或其他方法:)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    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();
      }
    }


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


    不,完全不对。对不起,但不是。那幅画上说一切都是一种饮料,在某些情况下很多次都是这样。显然,其中一些白色三角形应该是钻石,大部分可能是黑色的。你唯一使用的关系是继承,这显然不是你的意图。

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

    回复评论:

    什么都没有,在需要的地方使用。当它被应用到不需要的地方时,会有一点。这里根本不需要。而且,你做错事了。奶油并不是咖啡的装饰物(我想这就是你想做的),加奶油的咖啡可能……但这仍然是过度设计的。

    一个装饰者为它正在装饰的东西添加行为。假设您有一个消息类和一个显示输出的打印函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct message
    {
      virtual void print() = 0;
    };

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

    现在假设您希望其中一些缩进。您可以通过使用"decorator"来实现这一点,decorator子类化并包含一条消息:

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

    看看它如何在不改变原始消息的情况下改变任何具体消息的行为?

    你的咖啡/调味品不是这样的。调味品就是你放在咖啡里的东西。一杯加奶油或不加奶油的咖啡在行为上没有区别(例如)。如果你想对加入的奶油收费,那么你只需把它和咖啡杯联系起来,当你问咖啡杯多少钱的时候,就可以算出总数。