【设计模式之美】<Reading Notes>继承与组合

191 阅读3分钟

继承缺点
继承是面向对象的四大特性之一,用来表示类之间的 is-a 关系,可以解决代码复用的问题。虽然继承有诸多作用,但继承层次过深、过复杂,也会影响到代码的可维护性。在这种情况下,我们应该尽量少用,甚至不用继承.
使用组合替换继承
继承主要有三个作用:表示 is-a 关系,支持多态特性,代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成。

设计一个关于鸟的类,使用组合、接口、委托完成

定义为一个抽象类 AbstractBird,麻雀、鸽子、乌鸦等,都继承这个抽象类。由于不是所有鸟都会飞,所以不是所有鸟都有fly()这个方法。
我们可以从鸟会不会飞、会不会叫来对鸟进行划分。两个行为搭配起来会产生四种情况:会飞会叫、不会飞会叫、会飞不会叫、不会飞不会叫。如果假如新行为,我们可能还需要继续构建更加细分的抽象类,这样就比较麻烦。继承层次过深、继承关系过于复杂会影响到代码的可读性和可维护性。
可以利用组合(composition)、接口、委托(delegation)三个技术手段解决这个问题。
接口表示具有某种行为特性:
可以定义一个 Flyable 接口,只让会飞的鸟去实现这个接口。
对于会叫、会下蛋这些行为特性,我们可以类似地定义 Tweetable 接口、EggLayable 接口。
1、定义接口

public interface Flyable { 
	void fly();
}
public interface Tweetable { 
	void tweet();
}
public interface EggLayable { 
	void layEgg();
}

2、通过多个接口实现多个特性:鸵鸟不会飞,但是会叫会下蛋,所以只有两个接口。注意,mplements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用 。

public class Ostrich implements Tweetable, EggLayable {//鸵鸟 
	//... 省略其他属性和方法... 
	@Override public void tweet() { 
		//... 
	} 
	@Override public void layEgg() { 
	//... 
	}
}

3、接口只声明方法,不定义实现。也就是说,每个会下蛋的鸟都要实现一遍 layEgg() 方法,并且实现逻辑是一样的,这就会导致代码重复的问题。
可以针对三个接口定义三个实现类

实现了 fly() 方法的 FlyAbility 类、
实现了 tweet() 方法的 TweetAbility 类、
实现了 layEgg() 方法的 EggLayAbility 类。

public interface Flyable { 
	void fly();
	}
public class FlyAbility implements Flyable { 
	@Override public void fly() { 
	//... 
	}
}
//省略Tweetable/TweetAbility/EggLayable/EggLayAbility

然后,通过组合和委托技术来消除代码重复:

public class Ostrich implements Tweetable, EggLayable {//鸵鸟 
	private TweetAbility tweetAbility = new TweetAbility(); //组合 
	private EggLayAbility eggLayAbility = new EggLayAbility(); //组合 
	//... 省略其他属性和方法... 
	@Override public void tweet() { 
		tweetAbility.tweet(); // 委托 
	}
	@Override public void layEgg() { 
		eggLayAbility.layEgg(); // 委托 
	}
}

由于对Java不太熟悉,可能还需要关注一下这个知识点:
extends和implements区别