关于设计模式:Decorator方法,Java中的一个Decorator类型

Decorator method, one Decorator type in Java

我在学习使用装饰图案的过程中,遇到了一个我认为很简单但似乎找不到答案的问题。假设我有一个抽象的饮料类。那么让我们假设我有一些扩展饮料的具体成分:美式咖啡、浓缩咖啡、拿铁等等,还有一种抽象的调味品类扩展饮料。然后调味品类有多个子类:牛奶、糖、大豆、鞭子。每个调味品子类都有一个分别从饮料和调味品继承的cost和getdescription()方法。我的问题是,在测试时,我如何阻止某个饮料实例使用同一类型的多种调味品,也就是说,美国的大豆只需收费一次,即使大豆在测试课上说了两次。我知道我可以将调味品保存到一个列表中,并在添加新的调味品时检查它是否存在,我只是想看看是否存在更好的选择。

饮料类

1
2
3
4
5
6
7
8
9
10
11
public abstract class Beverage {

    String description ="Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();

}

调味品装饰师

1
2
3
4
5
public abstract class CondimentDecorator extends Beverage {

    public abstract String getDescription();

}

暗黑类

1
2
3
4
5
6
7
8
9
10
11
public class DarkRoast extends Beverage {

    public DarkRoast() {
        description ="Dark Roast Coffee";
    }

    public double cost() {
        return .99;
    }

}

大豆类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Soy extends CondimentDecorator {

    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    public String getDescription() {
        return beverage.getDescription() +", Soy";
    }

    public double cost() {
        return .15 + beverage.cost();
    }

}

如果有人能帮助我,甚至给我指一篇好文章或一个教程,我会非常感谢。


听起来像头一设计模式(HFDP)的例子?测试用例很容易理解,但是方法可能不太多。

把装修工想象成包装工。当一个装饰器要包装某个东西时,它可以检查那个"东西"是否已经包含了它自己类型的装饰器。以下是我稍微修改过的HFDP代码:

1
2
3
4
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Soy(beverage2);     // wrap once
beverage2 = new Soy(beverage2);     // wrap again (**error case)

您必须决定是否要禁止所有装饰器使用多个包装,或者某些装饰器可能具有一种"仅一次"属性。另一件要决定的事情是,如果发生第二次换行(上面注释中的**),是否失败(抛出异常),或者您只想忽略EDOCX1中的额外换行(0)。

如果您在包装时停止多次包装,它可能更干净,更不容易出现错误。在构造函数中。您可以在抽象类中编写一个通用函数,该函数使用反射(在不支持反射的语言中不起作用),或者解析被包装对象的描述以找到它自己的字符串(如果装饰没有唯一的名称,则不太可靠)。

我看到的最大的问题是调味品包装饮料,而且根据设计(信息隐藏),调味品不"知道"他们包装其他调味品。您编写的任何代码都可能是脆弱的(它可能违反开放-关闭原则)。然而,这就是设计上的权衡。你不能拥有所有东西,所以决定什么更重要(停止多重包装,或者设计一个允许添加新的装饰而不破坏任何东西)。

UML Diagram of the key classes

如果您可以依赖格式来标识嵌套,那么使用getdescription(解析它)可能是最有意义的。

大豆类可以做到这一点:

1
2
3
4
5
6
7
private String myDescription ="Soy"
public Soy(Beverage beverage) {
    if (beverage.getDescription().contains(myDescription)) {
        throw new Exception();
}
    this.beverage = beverage;
}

但更好的方法可能是在","字符上使用.split(),并检查这些字符串,因为描述只是使用逗号(在getDescription()中)进行连接。

如我所说,如果禁止所有多个调味品包装是一个一般规则,那么您可以将此逻辑重构为ConditionTDecorator类,以避免代码重复。甚至可以使用decorator-boolean属性来表示"allowmultiple"并对其进行编码。