我可以通过添加子类来解决添加功能的问题,那么我为什么要使用修饰模式修饰模式呢?
灵活性,这就是我说的原因。
从 维基百科的修饰模式
修饰模式可以用来 使扩展(装饰)成为可能 某一物体的功能 在 运行时间。
修饰模式的全部意义在于动态地添加额外的行为/功能,这在设计时当然是不可能的。
来自同一篇文章:
修饰模式是 替代子类化。 子类化增加了编译时的行为 时间 ,并且更改将影响所有 原始类的实例; 装饰可以提供新的行为在 单个对象的运行时 .
来自 GoF 的一个例子:
假设您有一个 TextView 类。然后在需要滚动文本视图的位置,从 TextView 子类化并创建 ScrolledTextView 类。在其他地方,您需要在文本视图周围添加边框。所以您再次子类化并创建 BorderedTextView。现在在你想要边框和卷轴的地方。前两个子类都没有同时具有这两种能力。所以你需要创建第三个。在创建 ScrolledBorderedTextView 时,实际上是在复制工作。如果您有任何组合前两个功能的方法,那么就不需要这个类。事情可能会变得更糟,这可能会导致不必要的班级爆发。
基本上,通过使用这个修饰模式,你可以在 RUNTIME 中为 object 添加任意数量的额外责任,这是不可能通过子类化来实现的,除非潜在地破坏你的代码结构或者为子类添加许多重复的组合。
但有一件事 设计模式不是必须使用的东西。 无论是否需要模式是 取决于你的特殊问题,您都希望长时间地维护代码,无论您是否希望扩展以及其他诸如此类的因素。 还有 没有在所有情况下都有用的模式。 适合某种情况的图案(装饰图案或其他图案)可能不是另一种情况的好选择。
设计模式书指出了使用 Decorator 相对于子类化的两个主要优势:
比静态更灵活 继承修饰模式 提供了一种更灵活的方式来添加 对物体的责任 具有静态(多重) 继承遗产。与室内设计师, 责任可以增加和 在运行时仅通过 连接和分离它们。在 相反,继承需要 为每个人创建一个新类 额外责任(例如: 文本视图, BordedTextView) 并增加了 系统的复杂性。此外, 提供不同的装饰 特定组件的类 类让你混合和匹配 责任。 装饰者也使添加变得容易 属性两次。例如,到 给 TextView 一个双边框, 只需附加两个边框装饰器。 继承边界类两次 最多也就是容易出错 避免高级别的特性加载类 在层次结构中。 Decorator 提供了一个 现收现付方法 责任,而不是尝试 支持所有可预见的特征 在一个复杂的、可定制的类中, 您可以定义一个简单的类和 增量地添加功能 修饰器对象,功能可以 由简单的片段组成 结果,申请人无须缴付费用 它没有使用的功能。它是 也很容易定义新的 独立于 它们扩展的对象类,甚至 不可预见的延长。延长 复杂类倾向于公开 不相关的资料 你增加的责任
比静态更灵活 继承修饰模式 提供了一种更灵活的方式来添加 对物体的责任 具有静态(多重) 继承遗产。与室内设计师, 责任可以增加和 在运行时仅通过 连接和分离它们。在 相反,继承需要 为每个人创建一个新类 额外责任(例如: 文本视图, BordedTextView) 并增加了 系统的复杂性。此外, 提供不同的装饰 特定组件的类 类让你混合和匹配 责任。
装饰者也使添加变得容易 属性两次。例如,到 给 TextView 一个双边框, 只需附加两个边框装饰器。 继承边界类两次 最多也就是容易出错
避免高级别的特性加载类 在层次结构中。 Decorator 提供了一个 现收现付方法 责任,而不是尝试 支持所有可预见的特征 在一个复杂的、可定制的类中, 您可以定义一个简单的类和 增量地添加功能 修饰器对象,功能可以 由简单的片段组成 结果,申请人无须缴付费用 它没有使用的功能。它是 也很容易定义新的 独立于 它们扩展的对象类,甚至 不可预见的延长。延长 复杂类倾向于公开 不相关的资料 你增加的责任
在我看来,仅仅防止子类爆炸是相当有说服力的。
如果你有一个 TextWindow,你想添加水平滚动,垂直滚动和边框所有独立的和可选的,使用子类,你必须为 HorizontalScrollingTextWindow,VerticalScrollingTextWindow,HorizontalAndVerticalScrollingTextWindow,BorderedTextWindow,HorizontalScrollingBorderedTextWindow,VerticalScrollingBorderedTextWindow,HorizontalAndVerticaScrollingBorderedTextWindow定义子类,如果你关心滚动和边框的顺序。
TextWindow
HorizontalScrollingTextWindow
VerticalScrollingTextWindow
HorizontalAndVerticalScrollingTextWindow
BorderedTextWindow
HorizontalScrollingBorderedTextWindow
VerticalScrollingBorderedTextWindow
HorizontalAndVerticaScrollingBorderedTextWindow
使用 Decorators,您只需定义两个滚动修饰符和一个边框修饰符。
子类化可能导致 Liskov代换原则出现问题。装饰器可以避免这种情况。
装饰器的另一个优点是,您正在对接口进行写入(强制写入)。这使得测试更加容易。的确,您的对象层次结构也可以写入接口,因此具有一些相同的优点,但是,我可以单独测试装饰类的单个实现。我不能对子类做同样的事情,因为我总是将整个层次结构返回到基类。我无法单独测试新代码。
使用修饰模式和遵循单一责任原则,我可以创建几个装饰器,并按照自己的意愿堆叠它们。我可以在运行时配置它。在继承中,我要么必须创建每一个可能的分支(a-> b-> c,然后 a-> c-> b,因此复制代码并增加测试数量) ,要么创建1个层次结构,然后在需要时添加另一个层次结构,但这会触发一个新的测试/发布周期。
这就是为什么要使用修饰模式而不是子类化。
下面是基于实际实现的差异。
装饰是子类化的一种替代方法,用于扩展现有类的功能。下面是一些我们应该使用子类化或者修饰符的场景。
1)子类化主要用于扩展类似情况下的功能 一组想要保留旧功能和新功能的类 在子类中 子类的所有实例共享相同的功能 对子类进行更改,然后它将反映所有的实例 儿童班。例如 层次关系关系,类的相似组。
父母-> 子女-> 孙子。
Car-> Maruti 800-> Maruti 100(将具有 Maruti 800以及 New 的功能)
2)修饰模式是用来装饰现有的类而不改变旧的行为。比如一个类圆有平面边框但是我们需要用红色边框来装饰它,过一段时间后,有些用户想要黄色的圆,有些用户想要绿色边框的圆,有些用户想要红色和黄色边框的圆,有些用户想要红色和绿色边框的圆等,这是一个完美的图案,因为它不会减少组合类。下面是一个例子。
Icircir = new RedDecorator (new Circle ())用红色装饰圆圈
ICircle cir = new YellowDecorator (new Circle ())用黄色装饰圆圈
Icircir = new RedDecorator (new YellowDecorator (new Circle ()))
用红色和黄色圆圈,在这里我们不需要创建红色和黄色类装饰器。同样地,我们可以用其他的组合集来装饰这个圆,而不需要创建新的组合类集。
这样就减少了组合类的 no。
以下是有关修饰模式的有用连结
Https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm