学习 JVM 的时候遇到了组合的概念,回想了下这是面向对象的知识,有点遗忘,所以写下这篇文章加深记忆。
首先,继承和组合都是实现类复用的重要手段,但继承的坏处是会破坏封装性,而采用组合的方式能提供更好的封装性。
继承(is-a) VS 组合(has-a)
下面以组合和继承的综合例子来介绍它们的区别:
要实现的目标:鸟(Bird)和狼(Wolf)都是动物(Animal),动物都有心跳(beat()),会呼吸(beat()),但是鸟会fly(fly()),狼会奔跑(run()),用java程序实现以上描述。
class Animal {
private void beat() {
System.out.println("心脏跳动...");
}
public void breath() {
beat();
System.out.println("吸一口气,呼一口气,呼吸中...");
}
}
// 继承Animal,直接复用父类的 breath() 方法
class Bird extends Animal {
// 创建子类独有的方法fly()
public void fly() {
System.out.println("我是鸟,我在天空中自由的飞翔...");
}
}
// 继承Animal,直接复用父类的 breath() 方法
class Wolf extends Animal {
// 创建子类独有的方法 run()
public void run() {
System.out.println("我是狼,我在草原上快速奔跑...");
}
}
public class InheritTest {
public static void main(String[] args) {
// 创建继承自 Animal 的 Bird 对象新实例 b
Bird b = new Bird();
// 新对象实例b可以breath()
b.breath();
// 新对象实例b可以fly()
b.fly();
Wolf w = new Wolf();
w.breath();
w.run();
/*
* ---------- 运行 Java 程序 ----------
* 心脏跳动... 吸一口气,呼一口气,呼吸中...
* 我是鸟,我在天空中自由的飞翔...
* 心脏跳动... 吸一口气,呼一口气,呼吸中...
* 我是狼,我在草原上快速奔跑...
*
* 输出完毕 (耗时 0 秒) - 正常终止
*/
}
}
// 使用组合方式实现目标
class Animal {
private void beat() {
System.out.println("心脏跳动...");
}
public void breath() {
beat();
System.out.println("吸一口气,呼一口气,呼吸中...");
}
}
class Bird {
// 定义一个 Animal 成员变量,以供组合之用
private Animal a;
// 使用构造函数初始化成员变量
public Bird(Animal a) {
this.a = a;
}
// 通过调用成员变量的固有方法(a.breath())使新类具有相同的功能(breath())
public void breath() {
a.breath();
}
// 为新类增加新的方法
public void fly() {
System.out.println("我是鸟,我在天空中自由的飞翔...");
}
}
class Wolf {
private Animal a;
public Wolf(Animal a) {
this.a = a;
}
public void breath() {
a.breath();
}
public void run() {
System.out.println("我是狼,我在草原上快速奔跑...");
}
}
public class CompositeTest {
public static void main(String[] args) {
// 显式创建被组合的对象实例 a1
Animal a1 = new Animal();
// 以 a1 为基础组合出新对象实例 b
Bird b = new Bird(a1);
// 新对象实例b可以 breath()
b.breath();
// 新对象实例b可以 fly()
b.fly();
Animal a2 = new Animal();
Wolf w = new Wolf(a2);
w.breath();
w.run();
/*
* ---------- 运行Java程序 ----------
* 心脏跳动... 吸一口气,呼一口气,呼吸中...
* 我是鸟,我在天空中自由的飞翔...
* 心脏跳动... 吸一口气,呼一口气,呼吸中...
* 我是狼,我在草原上快速奔跑...
*
* 输出完毕 (耗时 0 秒) - 正常终止
*/
}
}
总结:
继承和组合都可以实现代码的复用。
- 如果是 is-a 关系,一个类想要对另外一个类暴露所有接口,继承看起来是更好的选择。
- .如果是 has-a 关系,组合是更好的选择。
网友评论