与面向对象设计有关的组合是什么?


69

我听到(并在该站点上阅读了)很多关于“偏重于继承而非继承”的内容。

但是什么是Compositon?我从人:哺乳动物:动物的角度理解继承,但是我真的看不到任何地方的“组成”的定义。

Answers:


66

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

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


7
所以我可以这样说:“组成是当我创建一个Class A内部对象Class B(而不是Class B从中继承子类时Class A)。” ?
sumsumsign 2012年

哦。好的w69rdy,稍后再回答。是的,我可以这么说。
thesummersign

44

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


5
聚合是一种……关系。
汉克·霍尔特曼

1
聚合可以是简单的组合,或者如果它是类似事物的集合(例如,汽车上的轮子),则可以将其视为集合。一辆汽车可以有四个单独识别的车轮,也可以有一组车轮。这取决于用法。如果使用集合类,则集合本身就是一个聚合。
Cylon Cat 2010年

22

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


17
class Engine
{

}

class Automobile
{

}


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

}

组成-对象的功能由不同类的集合组成。实际上,这意味着持有一个指向要延迟工作的另一个类的指针。

继承-对象的功能由其自身的功能以及其父类的功能组成。

至于为什么首选组合而不是继承,请看一下Circle-ellipse问题


5

Composition的一个示例是在另一个类中有一个类的实例,而不是从该类继承

页面有一个很好的文章,解释为什么人们说:“在继承青睐组成”用的,为什么一些例子。


1
不是真正另一类[C1]内的类〔C2〕的一个实例,但一个实例中的类的实例另一个类的。前者可能会被误解,好像您在定义C1时实例化了C2一样,这不应该很常见。
Alois Mahdal 2014年

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,它会打印出“ Peeling具有吸引力。”,因为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()。除了扩展外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();
    }
}

有关更多信息,请 参考

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.