复合优先于继承
核心:
对于专门为了继承而设计并且具有很好的文档说明的类来说,使用继承是非常安全的。然而,对普通的具体类进行跨越包边界的继承,则是非常危险的。
论证
与方法不同的是,继承打破了封装性
咋的理解了?看如下的例子
public class A {
//方法功能是说话
public void say() {
//一堆说话的实现细节
}
//方法功能是唱歌
public void song() {
//具体的实现细节
// 唱歌的前提要先说话,所以调用自身的say()方法
say();
//然后一堆唱歌的实现细节
}
}
public class B extends A{
@Override
public void say() {
//我就想再说话之前先告诉大家一声我要说话了
System.out.println("我要开始说话了");
super.say();
}
@Override
public void song() {
//我就想再唱歌之前先告诉大家一声我要唱歌了
System.out.println("我要开始唱歌了");
super.song();
}
public static void main(String[] args) {
B b = new B();
//现在我要高歌一首
b.song();
}
}
结果
我要开始唱歌了
我要开始说话了
这结果怎么是这样子,我明明就是想唱歌呀,为啥又说我开始说话了,
其实是因为类B继承了类A ,在类A中的song实现细节中用了say()方法,导致在类B中先走了song()方法,又走了say()方法,但是对于类B来说我不想知道你走了啥方法,我就是想要的功能而已,唱歌就是唱歌,说话就是说话。当然我们也可以将类A中的song()方法改成不调用say()方法,但是这个不应该和类A的实现细节有关系啊,我只是提供了我的API接口,功能不变的情况下,我的内部实现细节怎么变都应该是没有问题的啊,所以这就是继承暴漏了API的实现细节出现的问题。
而复合就不会有这样的问题了,把类A做为一个私有域放入类B中。再调用类A的song()方法
public class B {
private A a;
public B(A a) {
this.a = a;
}
public void say() {
System.out.println("我要开始说话了");
a.say();
}
public void song() {
System.out.println("我要开始唱歌了");
a.song();
}
public static void main(String[] args) {
B b = new B(new A());
//现在我要高歌一首
b.song();
}
}
结果
我要开始唱歌了
可以看出复合屏蔽了类的实现细节,专注于功能而不关注细节
所以继承要严格的遵循is-a的关系,否则不能瞎用继承,在用继承的时候想好继承是不是只是为了扩展功能。否则就不得不用复合来代替继承了。