无法理解装饰者模式的主要缺点
Couldn't understand main drawback of Decorator Pattern
我正在学习Decorator Pattern
。这是一个非常强大的模式,而且非常有用。 objective 模式很简单,在运行时向对象添加(扩展)行为,无需通过组合和委托重新编译源代码,因此提供了子类化扩展行为的替代方案。但是,我阅读了这种模式的主要缺点之一,但并不能完全理解它。这是声明:
"People sometimes take a piece of client code that relies on
specific types and introduce decorators without thinking through
everything. Now, one great thing about me is that you can usually
insert decorators transparently and the client never has to know it's
dealing with a decorator. But like I said, some code is dependent on
specific types and when you start introducing decorators, boom! Bad
things happen."
取自"Head first design patterns"
作者"relying on specific types"是什么意思。如果可能的话,请用真实世界和简单的例子
有时您的代码会像 java
那样进行类型检查
a instanceof B
甚至
a.getClass == B.class
这种检查中断
如果a不再是B而是包裹着A的装饰器D
举个具体的例子:
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() 变体肯定会失败。
Jens 提供的出色答案无所不包;它完美地代表了作者和许多其他程序员的观点。
然而,这种观点已经过时且具有教育意义。
首先,Jens 使用的示例经常被引用,但它不是可以接受的装饰器的正确构造。装饰器或包装器的一些正确构造如下:
使用组合继承:
B 的实例
以这种方式组合 D 与 a 的任何比较仍然有效
装饰器 D 扩展 B 并实现接口 C(新功能)
a 可以指定为 D,并且仍然保持功能
使用接口封装:
接口 C 有一个装饰器 D(新功能),其方法 M 包装 D
A 扩展 B 并实现 C
A 也是 B 的一个实例并且保持功能
使用控制反转
这很相似,但不太明显,如果您熟悉 IoC
A 扩展 B 并且可以用 D 构造或注入,其功能由方法 M 包装。
A 的实例动态绑定到需要 B 的实例
此绑定适用于 B
的所有情况
D 的 d 个实例在 A 需要其功能的运行时注入
装饰器实现中的大部分缺陷是糟糕的设计或实现的错误。更多装饰器一直用于高可用性应用程序。
现实世界中的示例、每个 GUI、WYSWIG 编辑器、Web 浏览器和您可能会使用的 IDE,以及每个编程语言解释器、游戏引擎、照片编辑器、移动或网络应用程序,哦还有 CSS 和 SVG 渲染,事实上几乎所有现代渲染管道都是装饰器模式的特例。所以我想我是说,尊重装饰者。
我正在学习Decorator Pattern
。这是一个非常强大的模式,而且非常有用。 objective 模式很简单,在运行时向对象添加(扩展)行为,无需通过组合和委托重新编译源代码,因此提供了子类化扩展行为的替代方案。但是,我阅读了这种模式的主要缺点之一,但并不能完全理解它。这是声明:
"People sometimes take a piece of client code that relies on specific types and introduce decorators without thinking through everything. Now, one great thing about me is that you can usually insert decorators transparently and the client never has to know it's dealing with a decorator. But like I said, some code is dependent on specific types and when you start introducing decorators, boom! Bad things happen."
取自"Head first design patterns"
作者"relying on specific types"是什么意思。如果可能的话,请用真实世界和简单的例子
有时您的代码会像 java
那样进行类型检查a instanceof B
甚至
a.getClass == B.class
这种检查中断
如果a不再是B而是包裹着A的装饰器D
举个具体的例子:
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() 变体肯定会失败。
Jens 提供的出色答案无所不包;它完美地代表了作者和许多其他程序员的观点。
然而,这种观点已经过时且具有教育意义。
首先,Jens 使用的示例经常被引用,但它不是可以接受的装饰器的正确构造。装饰器或包装器的一些正确构造如下:
使用组合继承:
B 的实例
以这种方式组合 D 与 a 的任何比较仍然有效
装饰器 D 扩展 B 并实现接口 C(新功能)
a 可以指定为 D,并且仍然保持功能
使用接口封装:
接口 C 有一个装饰器 D(新功能),其方法 M 包装 D
A 扩展 B 并实现 C
A 也是 B 的一个实例并且保持功能
使用控制反转
这很相似,但不太明显,如果您熟悉 IoC
A 扩展 B 并且可以用 D 构造或注入,其功能由方法 M 包装。
A 的实例动态绑定到需要 B 的实例
此绑定适用于 B
的所有情况D 的 d 个实例在 A 需要其功能的运行时注入
装饰器实现中的大部分缺陷是糟糕的设计或实现的错误。更多装饰器一直用于高可用性应用程序。
现实世界中的示例、每个 GUI、WYSWIG 编辑器、Web 浏览器和您可能会使用的 IDE,以及每个编程语言解释器、游戏引擎、照片编辑器、移动或网络应用程序,哦还有 CSS 和 SVG 渲染,事实上几乎所有现代渲染管道都是装饰器模式的特例。所以我想我是说,尊重装饰者。