无法理解装饰模式的主要缺点

时间:2015-09-05 06:22:18

标签: design-patterns decorator

我正在学习Decorator Pattern。它是非常强大的模式,非常有用。模式的目标很简单,在运行时向对象添加(扩展)行为,而无需通过组合和委托重新编译源代码,因此为扩展行为提供了子类化的替代方法。但是,我读到了这种模式的一个主要缺点,并不能完全理解它。以下是声明:

  

"人们有时会接受一些依赖的客户端代码   特定类型和介绍装饰而不经过深思熟虑   一切。现在,关于我的一件好事是你通常可以   透明地插入装饰器,客户端永远不必知道它   处理装饰者。但就像我说的,一些代码依赖于   特定类型,当你开始介绍装饰,繁荣!坏   事情发生了。"

取自"掌握第一个设计模式"

作者的意思是"依赖于特定类型"。如果可能,请使用现实世界和简单的例子

2 个答案:

答案 0 :(得分:6)

有时您的代码会像java中那样进行类型检查

a instanceof B

甚至

a.getClass == B.class

这种检查中断

如果a不再是B而是装饰者D缠绕在A

举一个具体的例子:

I b = new B; // I is an interface implemented by B
b = D(b); // the decorator D implements I as well
doSomething(b);

doSomething定义如下:

doSomething(I i){
    if i instanceof B
        doThis();
    else
        doThat();
}

通过引入装饰器D,行为从doThis()更改为doThat()

一个真实世界的例子,这种情况发生在Hibernate上。它在某些情况下生成代理,这些代理提供额外的Hibernate特定行为。如果使用这些实体的代码确实如上所述进行检查,则它将根据创建的实体的方式而中断。实际上可能有效的实例,但getClass()变体肯定会失败。

答案 1 :(得分:1)

从Jens提供的优秀答案中解脱出来;它完美地代表了作者和许多其他程序员的观点。

然而,这种观点已经过时且具有教育意义。

首先,经常引用Jens使用的示例,但它不是装饰器的可接受正确的构造。一些正确的装饰器或包装器结构如下:

使用合成继承:

B的实例

以这种方式组合D与a的任何比较仍然有效

Decorator D扩展了B并实现了接口C(新功能)

a可以指定为D,但仍保持功能

使用界面封装:

接口C有一个装饰器D(新功能),其方法M包裹D

A扩展B和实施C

A也是B的一个实例,并且仍然有效

使用控件反转

这是类似的,但如果您熟悉IoC

,这会有点不太明显

A扩展B,可以构造或注入D,其功能由方法M包裹。

A的实例动态绑定到需要B

的实例

此绑定适用于B

的所有情况

d的实例在运行时注入,其中A

需要它的功能

在大多数情况下,装饰器实现中的缺陷是糟糕的设计或实现的错误。进一步的装饰器一直用于高可用性应用。

真实世界的例子,您可能会使用的每个GUI,WYSWIG编辑器,Web浏览器和IDE,以及每个编程语言解释器,游戏引擎,照片编辑器,移动或Web应用程序,哦和CSS以及SVG渲染,实际上几乎任何现代渲染管道都是装饰器模式的特例。所以我想我会说,尊重装饰师。