一个装饰模式用例来自" Head First:Design Patterns"书让我有这个问题。我会尝试写下来:
这是一个咖啡店系统,里面有一些咖啡和很多调味品 你可以加入它们(需要额外付费),你需要能够订购 和顾客所需的任何调味品一起喝咖啡,和 避免完全混乱(例如布尔人要跟踪 调味品)使用装饰图案。我们有一个抽象的饮料 类,每种类型的咖啡作为混凝土组件和每种调味品 作为包装Beverage的具体装饰器,如下所示:
因此我们有以下流程返回咖啡费用:
我的问题是:为什么不用列表而不是装饰器来实现它?我们可以在每个饮料中有一份调味品清单,并通过迭代列表来计算成本。要订购咖啡,我们只需要实例化一次并添加所需的调味品,避免声明如下:
// Using second image example
Beverage beverage = new DarkRoast(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
除此之外,我们还可以更灵活地进行操作,例如,一旦我们不让包装咖啡的装饰品给咖啡打折,不包括它的调味品。这是一个长期研究过的问题,我知道我错过了一些东西,或者说我错了,所以如果你对此有任何想法,我很想知道并进一步讨论。
答案 0 :(得分:3)
Decorator
模式是关于在运行时装饰(增强)具有附加功能的对象。
假设您已经有一个类,我们可以将其称为类A
,它实现了一个接口IA
。现在,如果需要添加我们希望它有一个方法的附加功能someAlignFeatureToA()
,A
中没有。现在您可以选择扩展类A
。另一种方法是Composition
,应优先于Inheritance
。您可以将类A
的对象包装在另一个类B
中,其中Interface
与A
相同,即IA
。对于客户端代码,这种方式很容易接受B类的对象,因为它具有与A相同的接口。(假设代码写得很好,取决于抽象(接口IA
)而不是关于类A
)这样的具体类。
这样,继承链不会太长,您可以在运行时轻松添加其他功能。
现在回答您的问题:在您提出的问题示例中,是的,列表更适合,但我觉得作者的意图是通过一个简单的示例来解释装饰器模式的用法。 假设咖啡由牛奶,咖啡混合物和糖组成。咖啡混合物进一步由较小的组分组成。这里的解决方案与Composite模式类似。
装饰器模式是基于行为增强的设计模式。 Java IO流使用这个,其中行为(方法实现)由装饰类增强(一个流用另一个流包装以增强前一个流)
答案 1 :(得分:1)
您应该将数据与行为区分开来。
装饰器是一个间接层,处理一个非常具体的问题。数据(在类型,合同,协议等方面)保持不变,但您在实施方面可以获得更大的灵活性。使用装饰器,您可以重用现有功能并开始添加更改 - 结合适配器,您可以基本上慢慢地开始从pone API迁移到另一个并保持兼容。
列表应仅负责以特定方式提供对包含数据的访问。对数据执行任务/处理列表中包含的数据应该由具有不同职责的函数/对象/类来完成。
答案 2 :(得分:1)
您所说的是一种计算饮料总成本的方法。更一般地说,你提出了另一种合并方式。执行Decorator Pattern假定的每个派生对象的主要行为,将它们添加到列表中。而且它也不是一个OO方法。
但Decorator Pattern的原始意图除此之外还有更广泛的视角。 动态地向对象添加扩展功能 。这意味着我有一些对象O1
&我希望它将它转换为另一个具有扩展功能的对象O2
和但是那两个是可以互换的。
因此,我们如何针对 Object Lifecycle 管理 对象演变 。希望你明白了。 :))
答案 3 :(得分:1)
免责声明:我使用的术语和代码段摘自this article。
您已经注意到,有两种可能的设计来“装饰”一个对象。两种方法都可以实现相同的功能,主要区别是使用环境。
Numbers numbers = new Sorted(
new Unique(
new Odds(
new Positive(
new ArrayNumbers(
new Integer[] {
-1, 78, 4, -34, 98, 4,
}
)
)
)
)
);
这是实现装饰器的最常见方法。我几乎专门以这种方式实现我的装饰器,因为我发现它是“自然”和简单的方式。当装饰器的数量不是太多(多少取决于程序员的解释)时,它非常适合。 当某些对象需要动态扩展时,它也很适合:当实例化时不知道需要哪些装饰器时。
Numbers numbers = new Modified(
new ArrayNumbers(
new Integer[] {
-1, 78, 4, -34, 98, 4,
}
),
new Diff[] {
new Positive(),
new Odds(),
new Unique(),
new Sorted(),
}
);
在极少数情况下(当装饰器的数量开始增长时),我做出设计选择,以将装饰器“扁平化”成类似列表的结构。这具有简化代码读取的优点,但具有在运行时失去灵活性的缺点:在对象创建时需要知道要应用哪个装饰器(在不变性的情况下尤其如此,我尝试着这种方法) ,因此根据应用程序的不同,这可能是不可行的。
最后,您只需要在灵活性和可读性之间做出设计决定即可。
答案 4 :(得分:0)
我知道我来不及了,但是我会尽力解释这一点。
我认为Head First本书中的示例很好地解释了这种模式,尽管许多人在找到简单的替代方案(例如该示例的集合)时感到困惑。
我认为更好的例子是送餐应用程序(WhySoHungry)。他们列出了
之类的多个优惠列表继续显示。
如果某人有资格获得多个要约,并且WhySoHungry希望他们同时应用所有要约,则计算部分将变得很困难。这是应用装饰图案的最佳位置。
答案 5 :(得分:0)
试图帮助OP理解装饰器正在扩展行为,这一点在第一本书的示例中并不明显。
通过freedev引用评论:
这与您必须扣除或增加的折扣或税款清单无关。它与您使用装饰器模式处理的行为有关。而且,相信我,与继承相比,使用列表处理这些行为更加困难。
在20个国家(= 20个租户)开展业务的多租户真实电子商务应用程序中的共享示例。
用例:构建一个可由UI呈现的购物车(或购物篮)对象。
要求:
现在,这几乎与描述的咖啡问题OP类似。口味太多(租户特定+页面特定行为)。
我们能做什么?根据需求,我们可以使用装饰器模式来装饰购物车。
可能的装饰器:
继续如下:
顶层可以根据租户和页面决定所有装饰者都需要参与其中。想象一下,通过一系列行为来做到这一点。现在很明显,列表将不是一个好主意。