使用组合而不是继承的好处是众所周知的;
哪种情况更好?
实际上,我可以看到强制基础构造函数的优势,但我想知道其他人对其他案例/域的看法。
答案 0 :(得分:3)
我相信着名的建议是"赞成作文而非继承"是在GoF设计模式书中创造的。
它说(第20页):
赞成对象组合而不是类继承。
理想情况下,您不必创建新组件以实现重用。 您应该能够获得所需的所有功能 通过对象组合来组装现有组件。但这是 很少这种情况,因为可用组件的集合永远不会 在实践中相当丰富。通过继承重用使得它更容易 制作可以用旧组件组成的新组件。遗产 和对象组合因此一起工作。
然而,我们的经验是设计师过度使用继承 重用技术,设计通常更容易重复使用(和 更简单)更多地依赖于对象组成。你会看到对象 组合在设计模式中反复使用。
请注意,此语句引用类继承,并且必须与接口继承区分开来,这很好。
<强>活力强>
两者都是实现可重用性的方法,但组合优于继承的优势在于活力。由于组合可以在运行时动态更改,这代表了一个很大的优势,而继承是在编译时静态定义的。
<强>封装强>
此外,组合基于使用组合对象的公共接口,因此对象彼此尊重公共接口,因此这促进了封装。另一方面,继承会破坏封装,因为子组件通常使用来自父组件的受保护接口。这是一个众所周知的问题,父类中的更改可能会破坏子类,即着名的基类问题。同样在继承父类中定义子类的物理表示,因此子类依赖于父类的演化。
<强>衔接强>
作文的另一个优点是它可以让课程专注于一项任务,也可以促进凝聚力。
<强>负债强>
显然组合问题是你会有更多的对象和更少的类。这使得设计可视化以及如何实现其目标变得更加困难。在调试代码时,除非您知道对象当前正在使用给定复合的确切实例,否则很难知道发生了什么。因此,在我看来,构图使设计更难理解。
由于组合的优点是多重的,这就是为什么建议它优于继承,但这并不意味着继承总是坏的。正确使用继承时,您可以获得很多优势。
有趣的参考资料
我建议对GoF Design Patterns进行一项研究,以查看两种类型可重用性的良好示例,例如使用合成的Strategy Pattern与使用继承的Template Method。
大多数模式都充分利用了接口继承,然后是对象组合来实现它们的目标,只有少数使用类继承作为可重用性机制。
如果你想深入研究这本书Holub on Patterns,那么在第2章中有一个名为“为什么extends
是邪恶的部分,可以更深入地研究阶级继承的责任。”
这本书提到了三个具体方面
- 失去灵活性:第一个问题是显式使用具体类名称会将您锁定在特定的实现中, 线下变化不必要的困难。
- 耦合:实现继承的一个更重要的问题是耦合,一个部分的不合需要的依赖 程序另一部分。全局变量是经典的例子 为什么强耦合是坏的。如果更改全局类型 变量,例如,使用该变量的所有代码 - 即 耦合到变量 - 可能会受到影响,因此所有这些代码都必须 检查,修改和重新测试。而且,所有使用的方法 变量通过变量相互耦合。那是, 一种方法可能会错误地影响另一种方法的行为 只需在尴尬的时间更改变量的值即可。这个 问题在多线程程序中特别棘手。
- 脆弱基类问题:在实现继承系统(使用扩展的系统)中,派生类紧密 耦合到基类,这种紧密连接是不可取的。 设计师们将这个绰号应用于“脆弱基类问题” 描述这种行为。基类被认为是“脆弱的”,因为 您可以以看似安全的方式修改基类,但这是新的 行为,当由派生类继承时,可能导致派生 班级失灵。
答案 1 :(得分:2)
我能想到的继承优于组合的唯一优势是它可以为您节省大量的锅炉板方法授权。
如果你真的有一个is-a关系和,你只需要子类中基类的所有方法,那么继承会免费为你提供所有这些方法。
答案 2 :(得分:0)
这是一个完整的争议或论证问题,也是一个广泛的问题。
AFAIK,当我们谈论容器(或)包含我们去Composition
的另一件事的东西时;即,实体包含另一个实体;这也提供HAS A
关系。示例:EntityA
有一个EntityB
。
请参阅Decorator design pattern
,该Composition
基于Inheritance
的概念。
但是当我们谈论IS A
时,我们会讨论EntityA
关系。即,EntityB
是EntityA
(或)EntityB
是<a href="/index.html">Home</a>
的类型
答案 3 :(得分:0)
当我发现继承最好的解决方案时,一个特殊情况是当我使用需要其他方法的运行时生成的类时。例如(在C#中):
public abstract class Rule{
/* properties here */
public Authorization Authorization { get; set; }
public abstract bool IsValid(dynamic request, User currentUser);
}
生成的模板:
public class Generated_1Rule : Rule{
public override bool IsValid(dynamic request, User currentUser){
// the user script is here
}
}
用户脚本示例:
return Authorization.IsAuthorized("Module_ID_001", currentUser);
好处是你可以为生成的脚本“编译”添加功能,并且它比从编译后继承自接口/组合更少。