74

我听到(并在这个网站上读到)很多关于“偏好组合胜过继承”的信息。

但是什么是合成?我从 Person : Mammal : Animal 的角度理解继承,但我在任何地方都看不到 Compostion 的定义。有人可以填写吗?

4

6 回答 6

72

组合是指将简单的类型组合成更复杂的类型。在您的示例中,组合可能是:

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

这种方法的优点是类型MammalPerson不必符合其先前父级的接口。这可能是一件好事,因为有时对超类的更改会对子类产生严重影响。他们仍然可以通过这些类的私有实例访问这些类的属性和行为,如果他们想公开这些以前的超类行为,他们可以简单地将它们包装在一个公共方法中。

我在这里找到了一个很好的例子:http ://www.artima.com/designtechniques/compoinh.html

于 2010-08-09T14:41:42.930 回答
46

组合只是构成整体的部分。汽车有轮子、发动机和座椅。继承是一种“是”的关系。组合是一种“具有”关系。

于 2010-08-09T14:39:08.177 回答
23

可以通过三种方式为类赋予行为。您可以将该行为写入类中;您可以从具有所需行为的类继承;或者您可以将具有所需行为的类作为字段或成员变量合并到您的类中。最后两种代表代码重用的形式,最后一种——组合——通常是首选。它实际上并没有为您的类提供所需的行为 - 您仍然需要在字段上调用该方法 - 但它对您的类设计的约束更少,并且更容易测试和调试代码。继承有它的位置,但组合应该是首选。

于 2010-08-09T14:44:03.553 回答
18
class Engine
{

}

class Automobile
{

}


class Car extends Automobile // car "is a" automobile //inheritance here
{ 
 Engine engine; // car "has a" engine //composition here

}

组合- 对象的功能由不同类的集合组成。在实践中,这意味着持有一个指向另一个工作被推迟的类的指针。

继承- 对象的功能由它自己的功能加上其父类的功能组成。

至于为什么组合优先于继承,请看一下圆椭圆问题

于 2010-08-09T14:46:23.767 回答
5

组合的一个例子是你在另一个类中有一个类的实例,而不是从它继承

这个页面有一篇很好的文章解释了为什么人们说“喜欢组合而不是继承”,并举例说明了原因。

于 2010-08-09T14:39:32.813 回答
0

作品

仅仅意味着使用引用其他对象的实例变量。


为了说明在代码重用部门中继承与组合的比较,请考虑这个非常简单的示例:


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运行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()。代替扩展FruitApple可以持有对Fruit实例的引用并定义自己的peel()方法,该方法只是peel()在 Fruit 上调用。这是代码:

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();
    }
}

更多信息 参考

于 2020-04-20T15:35:05.233 回答