Java 为什么在装饰者模式中使用抽象装饰者;有一个;及;是一个;抽象组件?

Java 为什么在装饰者模式中使用抽象装饰者;有一个;及;是一个;抽象组件?,java,decorator,Java,Decorator,我在许多与decorator模式相关的UML类图中看到,抽象decorator既包含抽象组件,又扩展了抽象类组件 例如,在第二个示例中,与以下链接中的咖啡制作场景相关: 类CoffeeDecorator扩展了抽象组件Coffee,并包含类型为Coffee的字段。为什么呢?这仅仅是一种情况,还是我们应该总是构建这样的“装饰系统”,为什么 我认为应该只需要CoffeeDecorator包含它想要修饰的组件,因为CoffeeDecorator实际上不是Coffee可以在任何可以使用包装对象的地方使用

我在许多与decorator模式相关的UML类图中看到,抽象decorator既包含抽象组件,又扩展了抽象类组件

例如,在第二个示例中,与以下链接中的咖啡制作场景相关:

CoffeeDecorator
扩展了抽象组件
Coffee
,并包含类型为
Coffee
的字段。为什么呢?这仅仅是一种情况,还是我们应该总是构建这样的“装饰系统”,为什么


我认为应该只需要
CoffeeDecorator
包含它想要修饰的组件,因为
CoffeeDecorator
实际上不是
Coffee
可以在任何可以使用包装对象的地方使用修饰器/包装器(“是一个”)。这就是为什么它必须扩展/实现类/接口

decorator/wrapper还有一个对它想要修饰/包装的实例的引用(“has”)

如问题中的链接示例所示,您可以编写:

 Coffee coffee = new WithMilk(new SimpleCoffee());

其中,
WithMilk
是一个装饰器。

装饰器和装饰类实现相同的接口。这允许装饰类向装饰类的每个方法(在接口中声明)添加额外的功能。这就是包装/装饰的意思

public class SimpleCoffee extends Coffee {
    @Override
    public double getCost() {
        return 1;
    }
    @Override
    public String getIngredients() {
        return "Coffee";
    }
}

public abstract class CoffeeDecorator extends Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    public double getCost() { // Implementing methods of the abstract class
        //you can add  extra functionality here.
        return decoratedCoffee.getCost();
    }

    public String getIngredients() {
        //you can add  extra functionality here.
        return decoratedCoffee.getIngredients();
    }
}
public class BiggerDecorator extends Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    public double getCost() { // Implementing methods of the abstract class

        return decoratedCoffee.getCost();
    }

    public String getIngredients() {

        return decoratedCoffee.getIngredients();
    }
}
Decorator类可以由较大的Decorator进行装饰

public class BiggerDecorator extends Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    public double getCost() { // Implementing methods of the abstract class

        return decoratedCoffee.getCost();
    }

    public String getIngredients() {

        return decoratedCoffee.getIngredients();
    }
}
您现在可以执行以下操作。类在此结构中

Coffee coffeeDecorator=new CoffeeDecorator(new Coffee);
Coffee biggerDecorator=new BiggerDecorator(coffeeDecorator);

为了多态性,decorator类必须实现该接口。

您的问题对我来说似乎有点循环……实现一个接口并使该接口的实例“装饰”是decorator模式的定义

所以你的问题有点像问“为什么装饰者模式使用装饰者模式?”

如果你正在包装的东西和你自己之间没有一个共同的接口,它只是一个has-a关系,或者,如果你正在将你正在包装的东西的接口调整到其他接口,它可能是适配器模式

我认为您真正的问题是“为什么decorator模式有用?是什么使得实现与您包装的东西相同的接口有用?”

装饰器模式允许对相同的行为进行变化

当您有一个系统必须处理一些细微不同的事情,但又不想知道复杂性,并且能够以相同的方式处理它们时,decorator模式的最大价值就来了。当然,这通常是我们从接口得到的。但在某些情况下,相同接口的多个实现并不是最佳解决方案……在经典示例中,假设您尝试为星巴克提供的每种类型的咖啡编写一个单独的类

您将从基础开始,但逐渐发现自己需要编写类,如
VentimoChachaiWhipWith肉桂onTop
。每次添加新的风味成分时,您都需要编写几十个新类,而每次删除一个新类时,就会有几十个类过时

另一方面,通过decorator模式,您可以为每种饮料创建一个单独的类,并使软件系统的大部分不必处理各种饮料类型;他们可以通过
饮料
界面对每种饮料进行相同的处理(无论它有多少附加组件)

当结账的人点击Chai按钮时,您只需制作一个
new Chai()
,由于其他属性可以添加到任意饮料中,您只需将其包装成
new Venti(饮料)
new Mocha(饮料)
new Whip(饮料)
new with肉桂(饮料)
,等。当客户要求加载项时,登记处的人员点击按钮

装饰图案的限制

这种模式有它的局限性,因为与每种调味品都使用一组布尔值相比,你更难反省自己到底喝了什么。我敢肯定,在为星巴克设计收银机时,它对跟踪饮料类型并没有什么实际用处,因为它不太擅长处理“哦,我指的是黑莓口味,不是覆盆子”…在decorator模式中支持这一点需要通过几个嵌套的饮料decorator进行内省,以找到需要删除的
Rasberry
decorator

它也不擅长处理复杂的相互关系……如果搅打头需要知道它与什么基本饮料类型相关,就需要知道它是否应该在价格上增加0.25美元,那么decorator模式就不是很有用了:decorator模式适用于各种装饰不需要知道它们包装的是什么具体实现的情况。当我们可以使用它时,它有助于减少耦合

我真正觉得有用的东西

我认为我们使用这个例子来解释decorator模式,主要是因为我们很容易谈论需要创建的类的数量的潜在指数节约,而许多实际的例子表明装饰有用,但实际上并没有使用decorator模式的属性

我能想到的案例是,在我们公司中使用了一些类似于该模式的东西,我们希望在流程中添加一些日志记录。例如,通过装饰EclipseIProgressMonitor,我们可以在每次显示的任务或子任务名称更改时检测,并记录流程的每个步骤花费的时间,以便知道哪些步骤花费的时间最多

作为一个装饰者,我们可以访问所有日志信息