为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?


当前回答

我看没有人提到钻石问题,这可能是继承带来的。

总之,如果类B和C继承了a,并且都重写了方法X,而第四个类D继承了B和C,并且没有重写X,那么应该使用X D的哪个实现?

维基百科对这个问题中讨论的主题提供了一个很好的概述。

其他回答

假设飞机只有两个部分:发动机和机翼。然后有两种设计飞机等级的方法。

Class Aircraft extends Engine{
  var wings;
}

现在你的飞机可以从固定机翼开始并在飞行中将其改为旋转翼。它本质上是有翅膀的发动机。但如果我想改变呢发动机也在飞行?

基类Engine公开一个赋值函数以更改其财产,或我将飞机重新设计为:

Class Aircraft {
  var wings;
  var engine;
}

现在,我也可以随时更换发动机。

我听过的一条经验法则是,当它是一种“is-A”关系时,应该使用继承;当它是“has-A”关系的时候,应该使用组合。尽管如此,我觉得你应该始终倾向于构图,因为它消除了很多复杂性。

如果你明白其中的区别,那么解释起来就更容易了。

程序代码

这方面的一个例子是不使用类的PHP(尤其是在PHP5之前)。所有逻辑都编码在一组函数中。您可以包含包含助手函数等的其他文件,并通过在函数中传递数据来执行业务逻辑。随着应用程序的增长,这可能很难管理。PHP5试图通过提供更面向对象的设计来解决这一问题。

遗产

这鼓励使用类。继承是OO设计的三大原则之一(继承、多态性、封装)。

class Person {
   String Title;
   String Name;
   Int Age
}

class Employee : Person {
   Int Salary;
   String Title;
}

这是工作中的继承。员工“是”个人或继承自个人。所有继承关系都是“is-a”关系。Employee还隐藏Person的Title属性,意思是Employee.Title将返回Employees的Title,而不是Person。

作文

组合比继承更受欢迎。简单地说,你应该:

class Person {
   String Title;
   String Name;
   Int Age;

   public Person(String title, String name, String age) {
      this.Title = title;
      this.Name = name;
      this.Age = age;
   }

}

class Employee {
   Int Salary;
   private Person person;

   public Employee(Person p, Int salary) {
       this.person = p;
       this.Salary = salary;
   }
}

Person johnny = new Person ("Mr.", "John", 25);
Employee john = new Employee (johnny, 50000);

组成通常是“有”或“使用”关系。这里Employee类有一个Person。它不从Person继承,而是将Person对象传递给它,这就是它“有”Person的原因。

继承上的组合

现在,假设您要创建一个管理器类型,这样您就可以:

class Manager : Person, Employee {
   ...
}

但是,如果Person和Employee都声明了Title,那么这个示例会很好用?经理头衔应该返回“运营经理”还是“先生”?在合成中,这种模糊性得到了更好的处理:

Class Manager {
   public string Title;
   public Manager(Person p, Employee e)
   {
      this.Title = e.Title;
   }
}

Manager对象由Employee和Person组成。标题行为取自雇员。这种显式组合消除了其他问题中的歧义,您将遇到更少的错误。

继承是非常强大的,但你不能强迫它(参见:圆-椭圆问题)。如果你真的不能完全确定一个真正的“is-a”子类型关系,那么最好是组合。

正如许多人所说的,我首先要检查是否存在“是”的关系。如果存在,我通常检查以下内容:

是否可以实例化基类。也就是说,基类是否可以是非抽象的。如果可以是非抽象的,我通常更喜欢写作

例如1。会计是员工。但我不会使用继承,因为Employee对象可以实例化。

例如2。书籍是SellingItem。SellingItem无法实例化-它是抽象概念。因此,我将使用遗传痤疮。SellingItem是一个抽象基类(或C#中的接口)

你觉得这种方法怎么样?

此外,我支持为什么要使用继承?

使用继承的主要原因不是作为组合的一种形式,而是为了让您可以获得多态行为。如果不需要多态性,那么可能不应该使用继承。

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

继承的问题在于它可以用于两个正交的目的:接口(用于多态性)实现(用于代码重用)

参考

哪个班级设计更好?继承与聚合