【发布时间】:2011-03-27 08:19:27
【问题描述】:
我听到(并在此网站上阅读)很多关于“偏好组合优于继承”的信息。
但是什么是合成?我从 Person : Mammal : Animal 的角度理解继承,但我在任何地方都看不到 Compostion 的定义。有人可以填写吗?
【问题讨论】:
标签: oop design-patterns language-agnostic composition
我听到(并在此网站上阅读)很多关于“偏好组合优于继承”的信息。
但是什么是合成?我从 Person : Mammal : Animal 的角度理解继承,但我在任何地方都看不到 Compostion 的定义。有人可以填写吗?
【问题讨论】:
标签: oop design-patterns language-agnostic composition
组合是指将简单的类型组合成更复杂的类型。在您的示例中,组合可能是:
Animal:
Skin animalSkin
Organs animalOrgans
Mammal::Animal:
Hair/fur mammalFur
warm-blooded-based_cirulation_system heartAndStuff
Person::Mammal:
string firstName
string lastName
如果你想完全组合(并摆脱所有继承),它看起来像这样:
Animal:
Skin animalSkin
Organs animalOrgans
Mammal:
private Animal _animalRef
Hair/fur mammalFur
warm-blooded-based_cirulation_system heartAndStuff
Person:
private Mammal _mammalRef
string firstName
string lastName
这种方法的优点是Mammal 和Person 类型不必符合其先前父级的接口。这可能是一件好事,因为有时对超类的更改会对子类产生严重影响。
他们仍然可以通过这些类的私有实例访问这些类的属性和行为,如果他们想公开这些以前的超类行为,他们可以简单地将它们包装在一个公共方法中。
我在这里找到了一个很好的示例链接:http://www.artima.com/designtechniques/compoinh.html
【讨论】:
Class B 中创建 Class A 的对象时(而不是从 Class A 子类化 Class B)。” ?
w69rdy 稍后回答。是的,我可以这么说。
组合只是构成整体的部分。汽车有轮子、发动机和座椅。继承是一种“是”的关系。组合是一种“具有”关系。
【讨论】:
可以通过三种方式为类赋予行为。您可以将该行为写入类中;您可以从具有所需行为的类继承;或者您可以将具有所需行为的类作为字段或成员变量合并到您的类中。最后两种代表代码重用的形式,最后一种——组合——通常是首选。它实际上并没有为您的类提供所需的行为 - 您仍然需要在该字段上调用该方法 - 但它对您的类设计的约束更少,并且更容易测试和调试代码。继承有它的位置,但组合应该是首选。
【讨论】:
class Engine
{
}
class Automobile
{
}
class Car extends Automobile // car "is a" automobile //inheritance here
{
Engine engine; // car "has a" engine //composition here
}
组合 - 对象的功能由不同类的集合组成。在实践中,这意味着持有指向另一个工作被推迟的类的指针。
继承 - 对象的功能由它自己的功能加上其父类的功能组成。
关于为什么组合优先于继承,请看Circle-ellipse problem。
【讨论】:
组合的一个例子是你在另一个类中拥有一个类的实例,而不是从它继承
This 页面有一篇很好的文章解释了为什么人们说“喜欢组合而不是继承”,并举例说明了原因。
【讨论】:
作曲
仅仅意味着使用引用其他对象的实例变量。
1- 通过继承编写代码
class Fruit {
// Return int number of pieces of peel that
// resulted from the peeling activity.
public int peel() {
System.out.println("Peeling is appealing.");
return 1;
}
}
class Apple extends Fruit {
}
class Example1 {
public static void main(String[] args) {
Apple apple = new Apple();
int pieces = apple.peel();
}
}
当您运行Example1 application 时,它会打印出“Peeling is有吸引力。”,因为Apple 继承(重用)Fruit 对peel() 的实现。但是,如果在将来的某个时间点,您希望将peel() 的返回值更改为Peel,您将破坏Example1 的代码。即使 Example1 直接使用 Apple 并且从未明确提及 Fruit,您对 Fruit 的更改也会破坏 Example1 的代码。
更多信息参考
这就是它的样子:
class Peel {
private int peelCount;
public Peel(int peelCount) {
this.peelCount = peelCount;
}
public int getPeelCount() {
return peelCount;
}
//...
}
class Fruit {
// Return a Peel object that
// results from the peeling activity.
public Peel peel() {
System.out.println("Peeling is appealing.");
return new Peel(1);
}
}
// Apple still compiles and works fine
class Apple extends Fruit {
}
// This old implementation of Example1
// is broken and won't compile.
class Example1 {
public static void main(String[] args) {
Apple apple = new Apple();
int pieces = apple.peel();
}
}
2- 通过组合编写代码
组合为Apple 重用Fruit's 实现peel() 提供了另一种方式。除了扩展Fruit,Apple 可以持有对Fruit 实例的引用并定义自己的peel() 方法,该方法简单地调用Fruit 上的peel()。代码如下:
class Fruit {
// Return int number of pieces of peel that
// resulted from the peeling activity.
public int peel() {
System.out.println("Peeling is appealing.");
return 1;
}
}
class Apple {
private Fruit fruit = new Fruit();
public int peel() {
return fruit.peel();
}
}
class Example2 {
public static void main(String[] args) {
Apple apple = new Apple();
int pieces = apple.peel();
}
}
更多信息ref
【讨论】: