第16条:复合优于继承
- 如果两个类的关系是 is-a,请选用继承。例如:男人/女人 与 人类的关系。
- 如果两个类的关系是has-a,请选用复合。例如:腿毛 与 人类的关系。
不恰当的继承会出现错误,不只是优雅不优雅的问题。
例如:
public class Son2<E> extends HashSet<E> {
public int count = 0;
public Son2(Set s ) {
}
@Override
public boolean add(E o) {
count++;
return super.add(o);
}
@Override
public boolean addAll(Collection c) {
count += c.size();
return super.addAll(c);
}
}
在此程序中, 如果调用addAll方法,count分别会在addAll和add方法中计算, 因为父类HashSet的addAll会执行add方法, 而子类又重写了add方法。
而Son2类只是想使用父类的方法, 它们的关系并不是父子关系,所以最合适的是使用复合。
例如:
public class Son3<E> {
public int count = 0;
private Set s;
public Son3(Set s) {
this.s = s;
}
public boolean add(E o) {
count++;
return s.add(o);
}
public boolean addAll(Collection c) {
count += c.size();
return s.addAll(c);
}
}
让Set成为类成员,被调用的add和addAll方法也不会覆盖Set的方法。
也可以专门新建一个类来转发(转发类),转发类也可以继承Set,把所有接口都转发。
类也可以包含多个这样的成员, 像不像Controll层调用多个Serivce层?
第17条: 要么为继承而设计,并提供文档说明,要么就禁止继承
如果类为继承而设计, 那么请提供详细的文档说明,要说明方法的用途(包括被子类重写后的影响),而不是实现过程;还要提供合适的钩子方法,在测试的唯一方式方法就是编写子类,发布前必须编写子类测试。
钩子方法是专门留给子类去实现的,并无方法体,例如:
父类:
子类: