【问题标题】:Couldn't understand main drawback of Decorator Pattern无法理解装饰器模式的主要缺点
【发布时间】:2015-09-05 06:22:18
【问题描述】:

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

“人们有时会使用一段 依赖的客户端代码 具体类型和引入装饰器不经过深思熟虑 一切。现在,关于我的一件好事是,你通常可以 透明地插入装饰器,客户永远不必知道它 与装饰者打交道。但就像我说的,一些代码 依赖于 特定类型,当您开始介绍装饰器时,轰隆隆!坏的 事情发生了。”

取自“Head first design patterns”

作者所说的“依赖特定类型”是什么意思。如果可能,请提供真实世界和简单示例

【问题讨论】:

    标签: design-patterns decorator


    【解决方案1】:

    有时您的代码会像在 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 Schauder - 我想再说一遍,尽管我发了帖子,但答案很好!说明性的,重点明确的:)
    【解决方案2】:

    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 的实例在运行时注入,它的功能是 A 需要的

    装饰器实现中的大部分缺陷是糟糕的设计或实现造成的。其他装饰器一直用于高可用性应用程序。

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

    【讨论】:

      猜你喜欢
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 2018-12-11
      • 1970-01-01
      • 2014-10-17
      • 1970-01-01
      • 2013-09-08
      相关资源
      最近更新 更多