聚合和继承的区别

聚合对于现在的我来说是一个比较晦涩难懂的点,但是由于他们两个能放在一起,我从继承的角度出发去理解聚合是什么!

面向对象有三个特征,继承、封装、多态。这三个的概念从我们学习Java开始就一直不停的出现,今天我就着重说一下继承!

首先我们来说一下什么是继承

继承的起源,是多个类中的相同特征和行为的抽象。

子类可以通过继承父类,那么可以调用父类中定义的方法和属性,从而达到代码重用的目的。另外,子类除了重用父类的代码以外,还可以扩展自身的属性和方法,来描述子类特有的特征和行为。

例如:人类和马类,有什么共同的特征和行为呢?都有年龄,都要呼吸...等等。我们可以把人类和马类相同的特征和行为抽取出来,形成一个父类:哺乳动物。人类和马类只要继承哺乳动物这个父类。那么,就可以直接重用哺乳动物父类中定义的属性和行为,相同的属性和行为就可以不用再重复描述。所以,通过继承,可以达到代码很大的重用。

除此之外,人类和马类还可以再定义自己本身的特征和行为。比如,马要吃草,人可以说话。那么,人类和马类可以在继承了哺乳动物父类的同时,再定义说话或吃草的行为,来扩展自身的特征和行为。使用继承有很多优点,父类的大部分功能可以通过继承关系自动进入子类;修改或扩展继承而来的属性和方法较为容易。那么,如何判断两个类之间是否有继承关系呢?很简单,用”是”来判断。比如:马是动物。那么马继承于动物。玫瑰是植物。那么玫瑰继承于植物。骂人的时候,我们会说:你是畜牲,那么这个人继承于畜牲,象畜牲一样没有人性。你是猪,那么这个人继承于猪,象猪一样愚笨。这就是所谓的”is-a”。

但是,继承同样有很多缺点。人类在大千世界中,不断的认识世界,也不断的改造世界。人类曾经梦想,在天空中飞翔。那么,人类如何飞行呢?鸟可以飞行,那是因为鸟有翅膀这个特征,才会拥有飞这个行为。如果人类继承于鸟类,象鸟一样长出翅膀,那么也就可以有飞行的行为了。

最近很火的封神电影中的雷震子,因其特殊的外貌出圈了,我们就拿雷震子举例。雷震子其实就是人类想在天空中飞行的一种想法。那就是继承鸟这个类,拥有鸟类的翅膀和飞行的行为。

那么,人类想下海应该怎么办呢?于是人类又在想,只要将人类继承于鱼类,拥有鱼的尾巴,那么就可以下海了。随着这种想法的产生,美人鱼就出现了。

那么,如果人类既想飞行,又想下海应该怎么办呢?有人说,那还不简单,把人类同时继承于鸟类和鱼类不就行了吗。但是,不好意思,在java中,类只能单根继承。也就是说,一个类只能有一个父类,不能同时继承两个父类。所以,从这里就可以看出继承的其中一个缺点:无法通过继承的方式,重用多个类中的代码。

除此之外,继承还有第二个缺点,那就是,父类的属性和方法,子类是无条件继承的。也就是说,不管子类愿意不愿意,都必须继承父类所有的属性和方法。比如,每个人都有自己父母,如果父母比较富有,那么子女就吃得好穿得好。如果父母是穷人,那么子女吃不饱穿不暖。很显然,所有的人都希望自己是富二代,官二代,而不想成为穷二代。不过,人是无法选择自己父母的。所以,父母的一切,自己是无条件接受的。

同样道理,如果人类继承于鸟类,我们希望拥有的是:鸟的翅膀和飞的行为。但是,鸟还有吃虫的行为,鸟还有下蛋的行为。这些是我们不希望拥有的。不过,如果人类继承于鸟类的话,那么吃虫和下蛋的行为,人类就得无条件接受了。所以,使用继承很容易造成方法的污染。一旦父类的属性和方法,在子类中不能完全适用。那么,也就不应该使用继承关系了。

class Bird{
	public void fly(){}
	public void layEggs(){}
}
class ManKind extends Bird{
public void speak(){}
}
……
ManKind m = new ManKind();
m.spaeak();
m.fly();//人类希望拥有的飞行行为
m.layEggs();//人类不希望拥有的下蛋行为

还有,从父类继承而来的实现是静态的,不能在运行时发生改变,不够灵活。比如,有一个人从春熙路到天府广场去。

class Man{
public void run(){
System.out.println("前往天府广场");
}
}
class CDMan extends Man{
}
……
CDMan c = new CDMan();
c.run();

当我们调用run()方法时,就只能打印从父类中继承的run方法。如果这个人想骑单车去天府广场应该怎么办呢?有人说,简单,在CDMan中重写run()不就行了吗?

class CDMan extends Man{
@Override
public void run(){
System.out.println("骑单车去天府广场");
}
}

但是,如果这个人想骑单车走一段,再开汽车走一段,就没法通过继承和重写来实现了。所以,无论是从父类中继承的方法,还是子类重写的父类方法,实现的都是一种静态的复用。不能在运行时发生改变,灵活性比较差。

那么,如何解决继承的这些缺点呢?荀子在《劝学》中,有这样的一段话:”假舆马者,非利足也,而致千里;假舟楫者,非能水也,而绝江河。君子生非异也,善假于物也。”就是说,人可以骑马,即使这个人跑得不快,也可以到达千里之外。人可以坐船,即使这个人不会游泳,也可以到达江河的任何位置。君子其实没什么太多特别的地方,只不过善于利用工具而已。这就是所谓的”has-a”。拥有什么,或者使用什么。

荀子的这段话,指出了解决继承缺陷办法,那就是使用聚合/组合达到代码的复用。比如,人想上天怎么办呢?可以利用飞机上天。人想下海怎么办呢,可以利用轮船下海。并不要求人要长出翅膀,人要长出鱼尾。

荀子的这段话,指出了解决继承缺陷办法,那就是使用聚合/组合达到代码的复用。比如,人想上天怎么办呢?可以利用飞机上天。人想下海怎么办呢,可以利用轮船下海。并不要求人要长出翅膀,人要长出鱼尾。

换句话说,用”has-a”(有什么或用什么)去替代”is-a”(是什么)。

class Plane{
public void fly(){}
}
class Ship{
public void swim(){}
}
class ManKind {
private Plane p = new Plane();
private Ship s = new Ship();
public void fly(){
p.fly();
}
public void swim(){
s.swim();
}
}

从以上代码可以看出,通过聚合/组合关系,可以解决继承的缺点。由于一个类可以建多个属性,也就是可以聚合多个类。所以,可以通过聚合/组合关系,重用多个类中的代码。

从以上代码可以看出,通过聚合/组合关系,可以解决继承的缺点。由于一个类可以建多个属性,也就是可以聚合多个类。所以,可以通过聚合/组合关系,重用多个类中的代码。

聚合/组合复用也可以在运行时动态进行。新对象可以使用聚合/组合关系,将新的责任委派到合适的对象。

//交通工具接口
interface Vehicle{
	public  void run();
}
//自行车实现类
class Bike implements Vehicle{
	public void run(){
		System.out.println("骑单车行走");
	}
}
//汽车实现类
class Car implements Vehicle{
	public void run(){
		System.out.println("开汽车行走");
	}
}
//人类
class CDMan {
	private Vehicle  v;
	public void run(){
		v.run();
	}
//更换交通工具
	public void changeVehicle(Vehicle  v){
		this.v = v;
	}
}
……
CDMan c = new CDMan();
c.changeVehicle(new Bike());
c.run();//骑单车行走
c.changeVehicle(new Car());
c.run();//开汽车行走

从这里可以看出,人类可以随时改换交通工具,达到行走的目的。这种方式可以在运行期间,随时改变接口属性的实现类。从而调用不同实现类描述的具体方法,灵活性很强。

总结:

继承和聚合/组合都可以达到代码重用的目的。继承有自身的优点,父类的大部分功能可以通过继承关系自动进入子类;修改或扩展继承而来的实现较为容易。

但是,继承同样有缺点,

  1. 无法通过继承达到多个类代码的重用。
  2. 父类的方法子类无条件继承,很容易造成方法污染。
  3. 从父类中继承的方法,是一种静态的复用。不能在运行时发生改变,不够灵活。

继承可以用,但使用继承需要谨慎。一般来说,使用继承有两个条件:

  1. 父类中所有的属性和方法,在子类中都适用。
  2. 子类不需要再去重用别的类中的代码。
    如果不能满足这两个条件,那么就应该使用聚合/组合关系去替代继承,来达到代码的复用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值