复合优先于继承
1.复合和继承的概念
继承:实现继承(当一个类扩展另一个类的时候)
复合:不扩展现有的类,而是在新的类中增加一个私有域,引用现有类的一个实例。
转发:新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回结果。
包装类:复合实例包装起来的实例的类
2.继承的缺点
子类继承父类时,子类可以重写父类的任意方法,打破了封装性(final等措施除外),因此继承用于包内部是安全的,但是用于跨越包边界的继承是危险的。
3.复合的优点
将现有类变成新类的一个组件(复合)
新类中的每个实例方法都可以调用现有类实例中的对应的方法(转发)(新类中的方法称为转发方法)
这样的类不依赖现有类的实现细节,现有类添加新方法,也不会影响新的类
//转发类
public class FowardSet<E> implements Set<E> {
//引用现有类的实例,增加私有域(复合)
private final Set<E> s;
public FowardSet(Set<E> s){
this.s= s;
}
/*
*转发方法
*/
@Override
public int size() {
return s.size();
}
@Override
public boolean isEmpty() {
return s.isEmpty();
}
@Override
public boolean contains(Object o) {
return s.contains(o);
}
@NotNull
@Override
public Iterator<E> iterator() {
return s.iterator();
}
@NotNull
@Override
public Object[] toArray() {
return s.toArray();
}
@NotNull
@Override
public <T> T[] toArray(T[] a) {
return set.toArray(a);
}
@Override
public boolean add(E e) {
return s.add(e);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return s.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return s.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
@Override
public void clear() {
s.clear();
}
@Override
public boolean equals(Object obj) {
return s.equals(obj);
}
@Override
public String toString() {
return s.toString();
}
@Override
public int hashCode() {
return s.hashCode();
}
}
/*
* 包装类(wrapper class),采用装饰者模式
*/
public class InstrumentedSet<E> extends FowardSet<E> {
private int addCount=0;
public InstrumentedSet(Set<E> set) {
super(set);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount+=c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
上面是书中的一个复合案例,他新增了一个类-转发类(FowardSet<E>),内部增加了私有域,并引用实例s(复合),然后再让包装类,也就是继承中的子类来继承使用。
简单来讲就是讲继承的子类上面,作为新类实例中一个组成部分来重写。(类似子类相当于发动机,转发类相当于车子,子类变成其中一部分,而不是完全相当于父类了)
设置继承的注意点
1.设计继承时,提供文档说明,要么禁止继承
2.文档说明必须有说明可覆盖的方法的自用性:
说明每个共有或受保护的方法,调用了哪些可覆盖的方法,以什么顺序调用,调用结果的影响等。
3.发布继承类(超类)之前,必须编写子类对父类进行测试(一般是3个子类左右进行测试)
4. 在超类中,不要在覆盖器调用可被覆盖的方法,因为之后子类中它重写该方法,那么初始化构造时,一开始还是会调用超类的原生可被覆盖方法。会出错。
5.对于并非要进行子类化的类,最好要编写禁止子类化:
1.声明类为final
2.将构造器都变成私有的,并增加公有的静态工厂代替构造器。