让我们来看个具体的例子: 我们假设程序用了个HashSet, 我们想统计一个HashSet对象创建以来总共加了多少个元素,为了实现这个功能,我们首先考虑继承,然后覆盖原有的实现加个计数:
package com.effective.item16; import java.util.Collection; import java.util.HashSet; public class InstrumentedHashSetextends HashSet { private static final long serialVersionUID = 1L; private int count = 0; InstrumentedHashSet() { super(); } @Override public boolean add(E e){ count++; return super.add(e); } @Override public boolean addAll(Collection extends E> c){ count += c.size(); return super.addAll(c); } public int getCount(){ return count; } }
结果发现但我们调用如下代码时,计数本应为3,真实结果为6. 什么原因呢? 原来父类HashSet里面addAll方法调用了add方法.这种内部细节,我们作为api的使用者很难知道,除非查看源码. 继而又有个严重的问题,如果HashSet的具体实现改变了,那么又有可能引起其他的问题,很难察觉.
让我们换种思路,采用复合代替继承.
import java.util.Collection; import java.util.Iterator; import java.util.Set; public class WrapperSetimplements Set { private int count = 0; private Set s; private WrapperSet(Set s) { super(); this.s = s; } ... @Override public boolean add(E e) { count++; return s.add(e); } @Override public boolean remove(Object o) { return s.remove(o); } @Override public boolean containsAll(Collection> c) { return c.containsAll(c); } @Override public boolean addAll(Collection extends E> c) { if(c != null) count += c.size(); return s.addAll(c); } ... }
Set接口保存了HashSet的功能特性,看似麻烦了些,但程序变得健壮了,这种Wrapper设计也带来了灵活性,他可以包装任何的Set实现, 同时又很好的扩展了计数器.
关于包装类,大家可以结合Decorator模式和Proxy模式做更深入的研究.