与面向对象设计相关的构成是什么?

时间:2010-08-09 14:37:04

标签: oop design-patterns language-agnostic composition

我听到(并在本网站上阅读)很多关于“赞成合成而非继承”。

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

6 个答案:

答案 0 :(得分:62)

组合是指组合简单类型以制作更复杂的类型。在您的示例中,组合可以是:

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

答案 1 :(得分:40)

构图只是构成整体的部分。汽车有轮子,发动机和座椅。继承是一种“是一种”关系。作文是一种“有”关系。

答案 2 :(得分:19)

有三种方法可以为类提供行为。你可以把这种行为写进班里;您可以从具有所需行为的类继承;或者您可以将具有所需行为的类作为字段或成员变量合并到您的类中。最后两个表示代码重用的形式,最后一个 - 组合 - 通常是首选。它实际上并没有为您的类提供所需的行为 - 您仍然需要在该字段上调用该方法 - 但它会减少对类设计的约束,从而使测试更容易,更容易调试代码。继承有其地位,但组成应该是首选。

答案 3 :(得分:16)

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

答案 4 :(得分:4)

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

This页面有一篇很好的文章解释了为什么人们会说“赞成作文而不是继承”,并附有一些原因。

答案 5 :(得分: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时,它将打印出“剥皮很吸引人。”,因为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的{​​{1}}实现的替代方法。 peel()可以保留对Fruit实例的引用,并定义自己的Apple方法,该方法仅在Fruit上调用Fruit,而不是扩展peel()。这是代码:

peel()

有关更多信息,ref