Java的组合(持有对象)与继承的区别

组合(对象持有)和继承都是实现系统功能重用,代码复用的最常用的有效的设计技巧,就是设计模式中的基础结构。

1. 区别1:

类继承允许我们根据自己的实现来覆盖重写父类的实现细节,父类的实现对于子类是可见的,所以我们一般称之为白盒复用。

对象持有(其实就是组合)要求建立一个好的接口,但是整体类和部分类之间不会去关心各自的实现细节,即它们之间的实现细节是不可见的,故成为黑盒复用。

2. 区别2:

继承是在编译时刻静态定义的,即是静态复用,在编译后子类和父类的关系就已经确定了。而组合是运用于复杂的设计,它们之间的关系是在运行时候才确定的,即在对对象没有创建运行前,整体类是不会知道自己将持有特定接口下的哪个实现类。在扩展方面组合比继承更具有广泛性。

3. 区别3:

继承中父类定义了子类的部分实现,而子类中又会重写这些实现,修改父类的实现,设计模式中认为这是一种破坏了父类的封装性的表现。这个结构导致结果是父类实现的任何变化,必然导致子类的改变。然而组合这不会出现这种现象。

对象的组合还有一个优点就是有助于保持每个类被封装,并被集中在单个任务上(类设计的单一原则)。这样类的层次结构不会扩大,一般不会出现不可控的庞然大类。而类的继承就可能出来这些问题,所以一般编码规范都要求类的层次结构不要超过3层。组合是大型系统软件实现即插即用时的首选方式。

总结如下表

组合(持有对象)

继承

黑盒复用

白盒复用

动态编译(在运行时确定实现类)

静态编译

保持封装性

破坏封装性

对象适配

 

4. 具体使用如何选择?

优先使用对象组合,而不是继承是面向对象设计的第二原则。

理想情况下,我们不需要创建新的组件来完成代码复用,而只需要通过对象组合的方法来拼装已存在的组件以获取新的功能。但这种情况很少出现,因为在实际情况中,现有的构建总是不够,而通过继承来复用代码往往要比通过组合对象容易得多。所以,继承和组合这两种方法并存于实际的软件开发过程中。

 

举个例子:通过Array类实现一个Queue类有两种方法:

第一种是通过继承的方式,把Queue类作为Array类的一个子类

Class Queue extends Array{}

第二种方法是将一个Array类型的实例作为Queue类的一个属性,通过对象组合来实现Queue

Class Queue extends Object{ private Array array;}

在这个例子中,继承的方式好一些,这样Queue类可以直接继承Collection类的方法和属性。如果用组合的方式,需要自己调用array来提供接口。

5. Adapter设计模式(对象适配器):

在想使用一个已经存在的类(如Target),但如果它的接口(也就是它的方法 Request)和你的要求不相同时,考虑使用适配器模式。

public class c10_1_4 {

	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Target t = new Adapter();
		t.request();
	}

}

/*
 * 要求Target具有specificRequest能力
 */
class Target{
	public void request(){
		System.out.println("common request");
	}
}

/*
 * 需要的方法
 */
class Adaptee{
	public void specificRequest(){
		System.out.println("specific request");
	}
}

/*
 * 适配器类 使Target具备所需要的方法
 */
class Adapter extends Target{
	//对象适配
	private Adaptee adaptee = new Adaptee();

	@Override
	public void request() {
		adaptee.specificRequest();
	}
	
}


 

猜你喜欢

转载自blog.csdn.net/u013905744/article/details/51752044