Java基础——组合(Composition)和聚合(aggregation) (转)

原文链接:https://blog.csdn.net/wuqinfei_cs/article/details/9249579

 

面向对象的编程概念:组合(Composition)和聚合(aggregation)

其实,你每天都能遇到合成(composition)技术。 它是大多数物理对象构建的方式。

假定以一个办公椅为例:
在大多数情况下,椅子是由座位、靠背、一些腿(四条腿)、轮子 组成的。
虽然椅子的某些变体可能将这些元素中的某些元素合并在一起,
但是,在大多数情况下,椅子并不是由一件材料组成的单一、离散的对象。
相反,它是一个多种(制造椅子所需要装备在一起的配件)元素的合成(composition).
本文就将讨论如何使用合成(composition)来创建你的对象。


继承(Inheritance)


单一职责原理( Single Responsibility Principle(SRP) ):
规定对象应该只有一个职责。
例如:
办公椅的轮子只负责使椅子便于滚动。
而不应期望轮子也来处理倚靠、升降座位高度以及旋转等任务。
说明:
首先,它可能是一个有着奇怪外形的轮子,
然而,从面向对象的角度来看,轮子可以有多种职责。
但是,多种职责使得对象的创建、测试以及维护都变得更加复杂,

同时也限制在别处重新使用该类的能力。

如果,一个轮子也处理倚靠的任务,那么它在不能倚靠的椅子上得不到使用。
重新使用是面向对象设计的一个主要目标。
结论:
因此,当你通过从另一个对象继承的方式来创建一个新的对象时,
目的就应该是:使得新的对象有一个相对于原始对象更加特定的版本。
例如:
使用继承(inheritance),你可以制造一个新类型的轮子:
1,它是由坚硬的塑料组成,可以在地毯上更好地滚动;
2,你还能制造一个在硬木地板上使用的由橡胶覆盖的轮子。
这些范例都是使用继承(inheritance)的很好的实践,
因为相应的实践结果的对象仍然是一个轮子,只是它是一个带有更加特定的类型轮子。
继承(Inheritance)是指创建一个有一( is a)的关系。
当做的对时,你就可以说"坚硬塑料的轮子也是一个轮子"。


由于SRP的存在,继承(inheritance)不应该用来给某个对象添加更多的责任。
因此,创建一个继承于轮子的新对象,然而却要添加一个喷射引擎就是一个不好的实践了。

你真的不能说一个喷射引擎是一个轮子。

 


合成(Composition)


合成(Composition )是指表达对象之间的关系。
回想一下那个椅子的范例吧。 椅子有一个座位。 椅子有一个靠背。 椅子有一套腿。
词组 "有一" 蕴含了一种椅子才具有的关系,或者至少说,使用,另外一个对象。
这种 "有一" 的关系正是合成(composition)的基础。


考虑一下下面的每一个椅子的部分以及椅子本身的类定义。
注意,为了简单一些,椅子的每一部分都只是空的对象。
但是,在真实的应用程序中,它们都有各自的属于它们自己的属性和方法。


package chairs.parts;
/** 座位 */
public class Seat {
// 座位的属性
// 座位的行为
}


package chairs.parts;
/** 靠背 */
public class Back {
// 靠背的属性
// 靠背的行为
}


package chairs.parts;
/** 椅子腿 */
public class Leg {
// (椅子)腿的属性
// (椅子)腿的行为
}




package chairs;
import chairs.parts.Back;
import chairs.parts.Leg;
/** 椅子 */
public class Chair {

protected Back back; // 靠背
protected Seat seat; // 座位
protected List<Leg> legs; // 腿

public Chair() {
back = new Back();
seat = new Seat();

legs = new ArrayList<Leg>();
legs.add( new Leg() );
legs.add( new Leg() );
legs.add( new Leg() );
legs.add( new Leg() );

}
}

 


在本范例中,Chair 对象本身是由一个 Back 类实例、一个 Seat 类实例以及四个 Leg 类实例组成的。
每一个对象都有着它自己的责任,然而,它们却在一个称为 Chair 的合成(composition)中关联在一起。


使用合成(composition)并不表明你就从不会用到继承(inheritance)。
事实上,当你开始合成一个新的对象时,你仍然经常需要选择一个合适的超级类(super class)。
假设你想要创建一个 OfficeChair 类,那么你可能从扩展 Chair 开始,然后添加一个允许座位自由旋转的额外组件。
或许这样说, OfficeChair 也是一个 Chair ,但是它有一个转环。


package chairs.parts;
/** 转环 */
public class Swivel {
// 转环的属性
// 转环的行为
}


package chairs;
import chairs.parts.Swivel;
/** 办公椅 */
public class OfficeChair extends Chair {
protected Swivel swivel;


public OfficeChair() {
swivel = new Swivel();
}
}

 

在上述代码中,明显地你同时使用了继承(inheritance)和合成(composition)来创建一个新的类。
这是你在构建复杂对象时最常用的使用方式。

 

聚集(Aggregation)


有一个与合成(composition)密切相关的概念称为聚集(aggregation)。
在社交中,合成(composition)与聚集(aggregation)之间的差异经常可以忽略。
但是,为了准确起见,我们将在这里进行讨论。


就像合成(composition)一样,聚集(aggregation)在一个对象由多个对象组成时才会发生。
但是,使用合成(composition)时,所有的内部对象(例如 Leg、Seat 以及 Back )都是有主对象(Chair)拥有的。
如果你破坏了Chair,那么可能你也破坏了 Leg、Seat 以及 Back 实例,
因为它们在一起时便形成了一个单一的 Chair 合成(composition)。


但是,假定你制造了一个称为 DinnerChair 的新的类型 Chair。
DinnerChair 扩展了 Chair,然而它也定义了一个涉及人当前坐在 DinnerChair里的属性。
因此,你可以说 DinnerChair 有一个 Person。


Chair 实例当然没有自己的 Person ,并且你也不会假设,如果 Chair 被破坏,Person也被破坏了。
此外,Person 的存在独立于 Chair, Person 可以离开该座位,并坐在另一个座位上。
这种独立性制造了大量的差异,因此这种对象的合并被称为聚集(aggregation),而非合成(composition)。
当设计你的应用程序时,注意差异是很重要的。


通常地,当使用合成(composition)时,对象会实例化它所含有的对象。
查看一下上述的 Chair 类,你可以看到 Back 、Seat 以及 Leg 在 Chair 类中被实例化了。
当使用聚集(aggregation)时,对象不会实例化它所含有的对象。
查看一下下面的代码,DinnerChair 类有一个 Person,但是它没有实例化该 Person。


package humans;
/** 人 */
public class Person {
// 人的属性
// 人的行为
}


package chairs;
import humans.Person;
/** 餐桌椅 */
public class DinnerChair extends Chair {

public Person person;

public function DinnerChair() {
super();
}
}

posted @ 2022-04-23 11:48  会飞的斧头  阅读(2167)  评论(0编辑  收藏  举报