【问题标题】:Decorator Pattern with Inheritance and Composition具有继承和组合的装饰器模式
【发布时间】:2014-03-25 21:29:29
【问题描述】:

也许我在这里遗漏了一些东西,我承认我的 OO 技能不是我想要的,但是看着这个 example of the decorator pattern,我注意到 UML 声明装饰器既是 is-a组件和有一个组件。这让我有点困惑,因为两者兼而有之似乎是多余的,事实上,当我测试“真实世界”代码found here,并将装饰器类修改为如下所示:

abstract class Decorator /*: LibraryItem*/ {
    protected LibraryItem libraryItem;

    // Constructor
    public Decorator(LibraryItem libraryItem) {
        this.libraryItem = libraryItem;
     }

     public /* override */ void Display() {
         libraryItem.Display();
     }
}

...

class Borrowable : Decorator {
    protected List<string> borrowers = new List<string>();

    // Constructor
    public Borrowable(LibraryItem libraryItem) : base(libraryItem) { }

    public void BorrowItem(string name) {
        borrowers.Add(name);
        libraryItem.NumCopies--;
    }

    public void ReturnItem(string name) {
        borrowers.Remove(name);
        libraryItem.NumCopies++;
    }

    public new /*override*/ void Display() {
        base.Display();

        foreach (string borrower in borrowers) {
            Console.WriteLine(" borrower: " + borrower);
        }
    }
}

我在这里所做的只是通过注释掉“:LibraryItem”来删除is-a关系,但通过保留“protected LibraryItem libraryItem;”来保持has-a关系。我还注释掉了 Display 方法上的覆盖,用 new 关键字替换了一个。据我所知,这与原始代码一样有效。

我在这里遗漏了什么吗?装饰器真的有必要从组件继承吗? UML 图和实现的代码肯定会建议这样做,但我很好奇是否有一些我没有看到或没有正确解决的问题。

想法?

【问题讨论】:

  • 无论我看你的代码多久,我都看不到这里的装饰器。装饰器的基本思想是将相同的接口递归应用到装饰对象。这里没有这样的东西。
  • 那是因为我提供的代码只是对上述链接之一中提供的真实装饰器的修改。 :)

标签: c# design-patterns decorator


【解决方案1】:

装饰器模式应该是

动态地为对象附加额外的职责。 装饰器为扩展子类提供了一种灵活的替代方案 功能。这种模式的设计使得多个装饰器可以堆叠在每个装饰器的顶部 其他,每次添加一个新功能到被覆盖的 方法。

实际上,如果您的装饰器类继承自它所装饰的同一类型,则可以链接越来越多的装饰器,从而增加越来越多的责任,同时仍然能够在调用原始基类的地方使用它。

例如,您将无法在示例中需要 LibraryItem 的地方传递 Borrowable。您的示例只是一个组合示例,而不是装饰器示例。

看看 C# Stream 及其装饰器。

例如,假设我有一个 Document 类。现在我可以有一个 DocumentWithSpellCheckingDecorator 和 DocumentWithGrammarCheckingDecorator。我可以使用两种装饰器,也可以只使用其中一种,但我仍然会有一个 Document 并且可以用作 Document,因为我只是用更多功能装饰了它。也许这不是一个完美的例子,但希望它有所帮助。

顺便说一句,dofactor 网站很容易理解,但有时并不能完全传达信息。我也在网站上的其他模式上发现了这一点。

【讨论】:

  • 好点。我非常关注 has-a 部分的冗余,以至于我没有考虑继承部分。当然,如果 'this' 关键字是只读的,这将不是问题。我想,如果我愿意,我可以手动将构造函数传递的对象中的每个属性分配给它的“this”对应部分,但是 UML 本身仍然将类定义为 is-a 和 has-a。
【解决方案2】:

装饰组件 IS-A 组件,例如BorderedImage 是一个带有边框的图像,它可以在任何使用它的Image 超类的地方使用。

这意味着BorderedImage 可以传递给任何使用Image 的代码,例如调用saveImage(Image image) 的方法。

打破BorderedImageImage 之间的继承关系将阻止在采用Image 的代码中使用装饰图像。

这意味着BorderedImage 将不再只是Image。因此,保持继承关系对于保持装饰者模式所要表达的意义至关重要。

【讨论】:

    猜你喜欢
    • 2021-07-18
    • 1970-01-01
    • 2014-03-07
    • 2012-09-04
    • 2012-07-24
    • 2011-03-01
    • 2011-03-23
    • 2015-05-27
    • 2020-06-03
    相关资源
    最近更新 更多