比继承更偏心组成?

为何更喜欢使用组合而不是继承? 每种方法都有哪些取舍? 何时应该选择继承而不是合成? java


#1楼

您想强迫本身(或另外一位程序员)坚持什么,以及什么时候容许本身(或另外一位程序员)拥有更多自由。 有人争辩说,当您想强迫某人处理/解决特定问题,以避免他们走错方向时,继承颇有用。 程序员

Is-aHas-a是有用的经验法则。 数据库


#2楼

个人通常经验法则: 在使用继承以前,请考虑组合是否更有意义。 安全

缘由: 子类化一般意味着更多的复杂性和联系性,即更容易更改,维护和扩展而不犯错误。 框架

Sun的蒂姆·布德罗Tim Boudreau)给出了更加完整和具体的答案jsp

在我看来,使用继承的常见问题是: 模块化

  • 无辜的行为会产生意想不到的结果 -经典示例是在初始化子类实例字段以前,从超类构造函数调用可重写方法。 在理想世界中,没有人会这样作。 这不是一个完美的世界。
  • 它为子类提供了不正确的诱惑,使他们没法对方法调用的顺序进行假设,而且这种假设-若是超类可能随时间演变,则这种假设每每不稳定。 另请参阅个人烤面包机和咖啡壶类比
  • 类变得更重 -您不必定知道您的超类在其构造函数中正在执行什么工做,或者将使用多少内存。 所以,构造一些无辜的轻量级对象可能比您想象的要昂贵得多,并且若是超类不断发展,这种状况可能会随着时间而改变。
  • 它鼓励子类的爆炸式增加 。 类加载会花费时间,更多类会消耗内存。 在处理NetBeans规模的应用程序以前,这可能不是问题,可是在那儿,咱们遇到了一些实际问题,例如菜单速度慢,由于菜单的首次显示会触发大量的类加载。 咱们经过使用更具声明性的语法和其余技术来解决此问题,但这也须要花费时间来解决。
  • 这使得之后更改内容变得更加困难 -若是您已公开一个类,则交换超类将破坏子类-这是一个选择,一旦您将代码公开,便成为您的选择。 所以,若是您不更改超类的实际功能,则能够在之后使用时有更大的自由来进行更改,而没必要扩展所需的内容。 以子类化JPanel为例-这一般是错误的。 若是子类在某个地方是公共的,则您将永远没有机会从新审视该决定。 若是以JComponent getThePanel()的形式访问它,则仍然能够执行此操做(提示:以您的API公开内部组件的模型)。
  • 对象层次结构没法扩展(或使它们在之后扩展比预先计划要困可贵多) -这是经典的“层数过多”问题。 我将在下面进行介绍,以及AskTheOracle模式如何解决它(尽管它可能会冒犯OOP纯粹主义者)。

... 函数

若是容许继承,那么个人见解是: ui

  • 除常量外,不公开任何字段
  • 方法应该是抽象的或最终的
  • 从超类构造函数不调用任何方法

... spa

全部这些对小型项目的影响要小于大型项目,对私人阶级的影响要小于公共项目。


#3楼

正如许多人所说,我首先要进行检查-是否存在“是”关系。 若是存在,我一般会检查如下内容:

基类是否能够实例化。 也就是说,基类是否能够是非抽象的。 若是不是抽象的话,我一般更喜欢构图

例如1.会计师是一名雇员。 可是我不会使用继承,由于能够实例化Employee对象。

例如2.书 SellingItem。 SellingItem没法实例化-这是抽象概念。 所以,我将使用Inheritacne。 SellingItem是一个抽象基类(或 C#中的接口

您如何看待这种方法?

此外,我支持@anon回答“ 为何要彻底使用继承?”中的内容。

使用继承的主要缘由不是做为一种组合形式,而是能够获取多态行为。 若是不须要多态性,则可能不该该使用继承。

@MatthieuM。 在https://softwareengineering.stackexchange.com/questions/12439/code-smell-inheritance-abuse/12448#comment303759_12448中

继承的问题在于它能够用于两个正交的目的:

接口(用于多态)

实现(用于代码重用)

参考

  1. 哪一个班级的设计更好?
  2. 继承与聚合

#4楼

我我的学会了始终偏向于继承而不是继承。 没有能够经过继承解决的程序化问题,而不能经过组合解决。 尽管在某些状况下可能必须使用Interfaces(Java)或Protocols(Obj-C)。 因为C ++一无所知,所以您必须使用抽象基类,这意味着您没法彻底摆脱C ++中的继承。

组合一般更合乎逻辑,它提供更好的抽象,更好的封装,更好的代码重用(尤为是在很是大的项目中),而且仅由于您在代码中的任何地方进行了孤立的更改,就不太可能在远处破坏任何内容。 这也使坚持“ 单一责任原则 ”变得更加容易,该原则一般被归纳为“ 一个类别发生变化的理由永远不仅一个。 ”,这意味着每一个类别都有一个特定的目的,所以应该仅具备与其用途直接相关的方法。 另外,因为继承树很浅,即便您的项目开始变得很大,也更容易保留概述。 许多人认为继承很好地表明了咱们的现实世界 ,但这不是事实。 现实世界中使用的构成多于继承。 几乎能够握在手中的每一个现实对象都是由其余较小的现实对象组成的。

可是,在组合方面也有缺点。 若是您彻底跳过继承而只关注合成,您会注意到,您常常不得不编写一些额外的代码行,若是您使用了继承,则这些代码行是没必要要的。 有时您也被迫重复本身,这违反了DRY原则 (DRY =不重复本身)。 一样,组合一般须要委托,而一个方法只是调用另外一个对象的另外一个方法,而此调用周围没有其余代码。 这种“双重方法调用”(可能会很容易扩展到三重或四重方法调用,甚至更远)比继承(继承人)要差得多,在继承中,您仅继承父方法。 调用继承的方法可能与调用非继承的方法同样快,或者可能稍慢一些,但一般仍比两个连续的方法调用快。

您可能已经注意到,大多数OO语言不容许多重继承。 虽然在不少状况下多重继承能够真正为您带来收益,可是这些都是例外,而不是规则。 每当您遇到“多重继承将是解决此问题的一个很是酷的功能”的状况时,一般您都应该从新考虑继承,由于即便这样,它也可能须要几个额外的代码行,基于组合的解决方案一般会变得更优雅,更灵活且更适合将来。

继承确实是一个很酷的功能,可是恐怕最近几年它已经被滥用了。 人们将继承视为能够钉牢这一切的锤子,不管它其实是钉子,螺钉仍是彻底不一样的东西。


#5楼

这两种方式能够很好地生活在一块儿,而且实际上彼此支持。

合成只是模块化地发挥做用:您建立相似于父类的接口,建立新对象并委托对其进行调用。 若是这些对象不须要彼此了解,那么它很是安全且易于使用。 这里有不少可能性。

可是,若是父类出于某种缘由须要为经验不足的程序员访问“子类”提供的功能,那么它彷佛是使用继承的好地方。 父类能够只调用它本身的抽象“ foo()”,该抽象被子类覆盖,而后能够将值提供给抽象基。

看起来不错,可是在不少状况下,最好给该类一个实现foo()的对象(甚至手动设置foo()所提供的值),而不是从某个须要继承的基类继承新类。要指定的函数foo()。

为何?

由于继承是传递信息的不良方法

这种组合在这里具备真正的优点:这种关系能够颠倒:“父类”或“抽象工做者”能够聚合实现特定接口的任何特定“子”对象+ 能够在任何其余类型的父内部设置任何子,它是type 。 而且能够有任意数量的对象,例如MergeSort或QuickSort能够对实现抽象Compare接口的对象列表进行排序。 换句话说,实现“ foo()”的任何对象组和能够利用具备“ foo()”的对象的其余对象组均可以一块儿玩。

我能够想到使用继承的三个真正缘由:

  1. 您有许多具备相同接口的类,而且想要节省时间编写它们
  2. 您必须为每一个对象使用相同的基类
  3. 您须要修改私有变量,在任何状况下都不能公开

若是这些是正确的,则可能有必要使用继承。

使用缘由1并无什么很差,在对象上具备可靠的接口是很是好的事情。 若是此接口简单且不会更改,则可使用组合或继承来完成此操做。 一般,继承在这里很是有效。

若是缘由是2,那就有点棘手了。 您真的只须要使用相同的基类吗? 一般,仅使用相同的基类是不够的,但这多是您的框架的要求,这是没法避免的设计考虑。

可是,若是要使用私有变量(状况3),则可能会遇到麻烦。 若是您认为全局变量不安全,则应考虑使用继承来访问也不安全的私有变量 。 提醒您,全局变量并不全是坏的-数据库本质上是一大组全局变量。 可是,若是能够处理,那就很好了。

相关文章
相关标签/搜索