【问题标题】:How do you implement the Decorator pattern if you can't inherit from the class you want to decorate?如果不能从要装饰的类继承,如何实现装饰器模式?
【发布时间】:2010-05-08 14:40:34
【问题描述】:

这更像是一个设计问题......如果:

1) 该对象被标记为“密封”,这意味着您不能从它扩展。 2) 或者你想覆盖一个方法,但它不是虚拟的。

那你能做什么?如果您没有源代码(如第 3 方库),则无法更改类源代码。

【问题讨论】:

    标签: c#


    【解决方案1】:

    装饰器模式不需要继承。最简单的情况是,如果您要装饰的对象实现了一个接口。然后你的装饰器可以简单地实现相同的接口:

    [   IWindow   ]
    [ + Draw()    ]
    ---------------
      |
      +--- [   Window    ]
      |    [ + Draw()    ]
      |
      |
      +--- [ DecoratedWindow ]
           [ + Draw()        ]
           -------------------
             |
             +--- [ BorderDecorator ]
             |
             +--- [ VerticalScrollbarDecorator ]
             |
             +--- [ HorizontalScrollbarDecorator ]
    

    现在你可以这样做了:

    IWindow w = new BorderDecorator(
                 new HorizontalScrollBarDecorator(
                  new VerticalScrollBarDecorator(
                   new Window(80, 24))));
    
    // This is a window with a border and scrollbars, even though
    // the original Window class has no idea what those are.
    w.Draw();
    

    可能的细节取决于您尝试装饰的类的确切性质。

    【讨论】:

    • +1 用于针对界面进行设计。如果类不可继承,这一点尤其很重要!
    • 实现接口的另一个好处是,当直接从类派生时,您可以覆盖受保护的方法,但不能实现它们或将它们委托回装饰对象。您可以通过覆盖它们并密封它们来解决这个问题,这样装饰器的工具就不会看到这一点,但这是额外且毫无意义的工作。界面还允许您挑选要公开的扩展点
    【解决方案2】:

    您可以采取多种方法,具体取决于您对代码的实际控制程度:

    1) 您是否将密封类的实例传递给第三方代码?如果是这样,除了祈祷您的第三方接受接口而不是具体对象之外,您无能为力。

    完全可以在 CLR 中编写钩子来拦截密封类中的方法,这就是一些模拟框架如何模拟非虚拟方法和密封类的方式。我建议这样做,因为您的第三方代码是针对对象的特定实现编写的,并且在第三方代码的鼻子下换出实现可能会产生不平凡的、未定义的后果。

    2) 您是否在自己的代码中使用密封类?如果是这样,那就去小工具包装吧!使用与您的密封类相同的公共方法创建一个非密封类,公共方法应该传递给您的密封对象,然后您可以根据需要覆盖您的类的功能。

    3) 您是否拥有密封类的代码?如果是这样,请找到类定义,双击“密封”关键字以突出显示它,根据需要按删除键;)或者可能将接受密封对象的方法换成具有相同公共方法的接口。

    【讨论】:

      【解决方案3】:

      您可以创建自己的类来包装第 3 方类并装饰该类。您的类将使用包装类实现。包装类充当适配器。

      【讨论】:

      • 但是你不能用代理类替换主题类,因为它们有不同的类型。
      • @ikke:是的。所以我没有说代理模式。
      • 那不是适配器而不是代理吗?
      • @Russ Cam 更正它是一个适配器。相应地改变了我的答案。
      【解决方案4】:

      如果您不能继承并且原始类没有实现您也可以在装饰器上实现的任何接口,那么我相信您应该尝试代理类或适配器。如果这也不可能,那么你总是有一个“替代技术资源”:你可以定义一个 conversion operator 允许将你的装饰器转换为原来的装饰器(尽管我认为这不是一个很好的选择)。

      【讨论】:

        猜你喜欢
        • 2021-07-18
        • 2015-05-27
        • 2011-11-20
        • 2019-10-02
        • 2020-02-29
        • 2012-02-16
        • 2012-09-04
        • 2011-03-23
        • 2012-07-24
        相关资源
        最近更新 更多