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


当前回答

简单地说,实现就像你(类)应该遵循的规则

但是扩展就像对很多类使用公共代码,也许只是其中一个需要重写。

其他回答

这条规则完全是无稽之谈。为什么?

原因是,在任何情况下,都可以区分是使用组合还是继承。这取决于对一个问题的回答:“是什么?是什么?”或“有什么?”其他的东西“。

你不能“宁愿”把某样东西变成另一样东西或拥有另一样。严格的逻辑规则适用。

也没有“人为的例子”,因为在任何情况下都可以给出这个问题的答案。

如果你不能回答这个问题,还有其他问题。这包括类的责任重叠,这通常是错误使用接口的结果,较少是通过在不同类中重写相同的代码。

为了避免这种情况,我还建议为类使用与它们的职责完全相似的好名称。

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

合成与继承是一个广泛的主题。对于什么更好,没有真正的答案,因为我认为这一切都取决于系统的设计。

通常,对象之间的关系类型为选择其中一个对象提供了更好的信息。

如果关系类型是“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组成。标题行为取自雇员。这种显式组合消除了其他问题中的歧义,您将遇到更少的错误。

理解这一点的一个简单方法是,当您需要类的对象与其父类具有相同的接口时,应该使用继承,这样它就可以被视为父类的对象(上广播)。此外,派生类对象上的函数调用在代码中的任何地方都保持不变,但要调用的特定方法将在运行时确定(即,低级实现不同,高级接口保持不变)。

当您不需要新类具有相同的接口时,即您希望隐藏该类的用户不需要知道的类实现的某些方面时,应使用组合。因此,组合更多的是支持封装(即隐藏实现),而继承意味着支持抽象(即提供某种东西的简化表示,在这种情况下,相同的接口用于具有不同内部结构的一系列类型)。