【问题标题】:Can decorators be a direct child of the component?装饰器可以是组件的直接子级吗?
【发布时间】:2015-02-28 09:16:39
【问题描述】:

查看维基百科 (http://en.wikipedia.org/wiki/Decorator_pattern) 上的装饰器模式页面,布局看起来像这样:

  • 组件接口
  • 组件实现
  • 实现组件接口的装​​饰器接口
  • 实现装饰器接口的装饰器

装饰器是否可以直接实现组件接口(并跳过装饰器接口)?

【问题讨论】:

  • 这个问题是关于哪种编程语言的?
  • 这应该适用于任何支持继承或接口的语言。为什么要问?
  • @neverendingqs 没有装饰器接口,有一个可选的 abstract 装饰器。
  • @qujck 接口不是纯抽象类(无论如何在功能上)?为什么一定要抽象装饰器?
  • @qujck 我也认为你回答了这个问题 =P

标签: design-patterns decorator


【解决方案1】:

使用装饰器模式获得的主要好处是,对于客户端代码来说,它是透明的。他们不需要了解装饰器,只需将其视为基本接口的普通旧具体实现即可:

ThingDoer thingDoer = new ThingDoerDecorator(new AwesomeThingDoer());
ThingDoer otherThingDoer = new AwesomeThingDoer();

thingDoer.doThing();
otherThingDoer.doThing();

这只是可能的,因为装饰器实现了与它正在装饰的东西相同的接口。如果它不实现这个接口,你就会失去这个模式的大部分功能。

但是,有时有一个基础装饰器实现是有意义的。

abstract class BaseThingDoerDecorator implements ThingDoer {
    private ThingDoer thingDoer;

    protected BaseDecorator(ThingDoer thingDoer) {
        this.thingDoer = thingDoer;
    }

    @Override
    public void doThing() {
        thingDoer.doThing();
    }
}

现在可以做这样的事情了:

ThingDoer thingDoer = new BaseThingDoerDecorator(new AwesomeThingDoer()) {
    @Override
    public void doThing() {
        System.out.println("This is an anonymous decorator");
        super.doThing();
    }
};

或者您可能希望所有装饰器都免受基础组件中的异常影响:

abstract class BaseThingDoerDecorator implements ThingDoer {
    // Same as above...
    // ...
    @Override
    public void doThing() {
        try {
            thingDoer.doThing();
        } catch (Exception e) {
            log(e);
        }
    }
}

【讨论】:

  • 只是为了澄清@Floegipoky,答案是:“是的,装饰器是组件的直接子级。但是,这里有一些你想使用抽象类的例子。”?
  • @neverendingqs 不,我的回答是装饰器必须实现组件接口,但这里有一些示例,它们可以继承 也来自基类。
  • 嗯这就是我的意思,但我的语法和措辞在这个问题中并不是最好的。无论如何,您的回复说明了我在寻找什么。
【解决方案2】:

这是您引用的维基百科页面中的 UML 图:

在这个图中,装饰器不是一个接口,而是一个抽象类。组件在此图中也不是一个接口(因为从它继承的 ConcreteComponent 和 Decorator 类没有图中箭头的虚线)。我知道这是一个细节,但 UML 是特定的原因。

也就是说,任何 ConcreteDecorator 都可以直接从 Component 继承(或者如果您的设计使用接口,则实现其接口)而不使用 Decorator 抽象类。但是,所有ConcreteDecorator 需要聚合一个组件,因此如果您有多个ConcreteDecorator,抽象类可能有助于避免重复代码。

【讨论】:

  • 我实际上认为图表是错误的。如果以这种方式实现模式,每个装饰器将有 2 个组件。避免不必要开销的唯一方法是使基本组件(装饰器“是”的那个)完全为空 - 这使其成为除名称之外的所有接口。这可能是一种将其引入没有接口的语言的有效方法,但它具有误导性,当然不应该用于该模式的权威模型。
  • 我只看到一个组件@Floegipoky。第二个在哪里?
  • 装饰器“包装”组件,所以我看不出接口代替装饰器有什么意义。这里的设计看起来像 GoF 中的设计,这是在 Java 或 C# 接口出现之前提出的。请引用您的权威参考,@Floegipoky
  • 根据这个图表,每个装饰器都有它聚合的组件(它正在包装的那个)和一个它作为继承的结果获得的组件。如果基础组件持有任何类型的状态,每个组件将持有 2 个副本。
  • 好的,所以您计算的是继承(“is a”)和聚合(“has a”)。 Java 的 java.io 类以这种方式使用装饰器,例如,BufferedInputStream 和 LineNumberInputStream 都扩展了 FilterInputStream,它充当抽象装饰器类。它们都是抽象类InputStream 的子类。理论上可能存在重复的状态,但我会说为了模式的利益而这样做是值得的。当然,我假设 Liskov 替换原则得到尊重。
猜你喜欢
  • 2016-01-13
  • 2020-05-20
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 2017-02-04
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多