前置知识
为了更好的理解“组合优于继承”这个概念,需要先知道下面这几个知识点:
类和类之间有三种关系:
a、继承
b、依赖(在B对象里有个方法,方法里面有个局部变量A,或者入参有A对象,叫做B依赖A)
c、关联(在B对象里面有个A对象作为成员变量,叫B关联A)
而”关联“可细分为:
1)组合(关系强,比如大雁和大雁的翅膀,两者密不可分)
2)聚合(关系比组合弱,比如大雁飞离雁群,但也可以再飞回来,两者关系没那么强)
前言
组合优于继承中的组合,就没细分了,就是指关联,即可以说:关联优于继承。
下面将用一个经典例子来简明阐述为什么组合会优于继承:
现在有一个需求:
使用HashSet设计一个新集合,集合中有一个count变量,用,来统计该集合总共存储过多少元素(包括被remove的)
- 思路一:继承HashSet,重写add()和addAll()方法
class MySet extends HashSet{
private int count = 0;
@Override
public boolean add(Object o) {
count++;
return super.add(o);
}
@Override
public boolean addAll(Collection c) {
count += c.size();
return super.addAll(c);
}
public int getCount(){
return this.count;
}
}
测试:
public static void main(String[] args) {
MySet mySet = new MySet();
mySet.add("a"); mySet.add("b"); mySet.add("c");
List<String> list = new ArrayList<>();
list.add("d"); list.add("e"); list.add("f");
mySet.addAll(list);
System.out.println("mySet.getCount() = " + mySet.getCount());
}
输出结果:
mySet.getCount() = 9
发现这个结果是错误的,正确结果是6才对。
原因是:HashSet中的addAll()方法回调了add()方法,导致多count++了
思考:
为了解决上面那个问题,我们可以addAll()方法不重写,这样输出的结果就是正确的了
但是这样做又会有新的问题:假如未来的HashSet类中的addAll()方法不再回调add()方法,那么MySet又出问题了
解决办法:重写addAll()方法,但这次我们不再调用父类的addAll()方法了,而是完全自己重写一个
这次似乎符合需求了,但还有问题是:
1、假如在未来的jdk版本中,新增了一个往集合添加元素的addSome()方法,
那么我们自己写的MySet方法又失效了(子类继承了addSome()方法却未统计元素的数量(count++))
2、HashSet类中难免有其他方法调用了add()方法和addAll(),而MySet重写了这两个方法,可能就会导致这些方法出现问题
解决办法:不再重写add()和addAll(),MySet新增两个方法add2()和addAll2()方法,代替add()和addAll()
最后,这样做勉强解决问题了,但是
1、未来在用这两个方法的时候,就得非常小心,不要用错了,只能调用add2()和addAll2(),不能使用add()和addAll()
2、万一在未来的HashSet类中,恰好新增了add2()和addAll2()这两个名字的方法,那MySet又出问题了
依据上述:
用继承虽然满足了需求,但终究有些勉强。那用组合试试?
用组合实现:
class MySet {
private Set set = new HashSet();
private int count = 0;
public boolean add(Object o) {
count++;
return set.add(o);
}
public boolean addAll(Collection c) {
count += c.size();
return set.addAll(c);
}
public int getCount(){
return this.count;
}
}
测试:
public static void main(String[] args) {
MySet mySet = new MySet();
mySet.add("a"); mySet.add("b"); mySet.add("c");
List<String> list = new ArrayList<>();
list.add("d"); list.add("e"); list.add("f");
mySet.addAll(list);
System.out.println("mySet.getCount() = " + mySet.getCount());
}
总结:
如果一步一步跟着上面的思路走,看到这里也就明白了为什么说:组合优于继承。
继承的特性,导致有些时候使用继承并不是一个完美的选择。”冰冻三尺,非一日之寒“平时在写代码的过程中,可以多留意一些
这些细节,但切记不可过分苛求。