通过对已有类的包装,使新类在增加功能的同时,实现对已有类的复用。
装饰者模式 实现代码复用较 继承的优点:
- 在类之间没有明确的is-a关系的提前下 利用继承,后续代码维护较难。例 Duck extends Bird ,虽然Duck目前 完全可以复用 Bird中的方法eat, drink, 然而后期需要为Bird添加 fly() 方法时,Duck 就自然而然的继承了这个不该有的方法,so Duck被污染了。所以,切记不用为了一时的偷懒,强行利用继承复用非is-a的类
public class Bird {
eat();
drink()
}
- 继承会在一定程度上 破坏类的封装性。子类依赖于 父类的实现细节
import java.util.Collection;
import java.util.HashSet;
public class InstrumentHashSet<E> extends HashSet<E> {
private int addCount = 0;
public InstrumentHashSet() {
}
public InstrumentHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@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;
}
}
import java.util.Arrays;
public class HelloTest {
public static void main(String[] args) {
InstrumentHashSet<String> s = new InstrumentHashSet<String>();
s.addAll(Arrays.asList("the", "one", "two"));
System.out.println(s.getAddCount());
}
}
猜猜输出为多少
|
|
|
|
|
|
|
|
|
|
6
你以为super.addAll(c) 在实现有超类方法一次调用完成,然而实现 细节上 超类的addAll 通过调用 add来完成。
跟踪一下代码:
(InstrumentHashSet) addAll —> AbstractCollection addAll
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e)) // 此处3次调用 实现类InstrumentHashSet 的 add
modified = true;
return modified;
}
从而addCount 在addAll 中+3 一次,在add中 +1 三次,所以为6.
由于继承依赖于细节,所以我们需要对超类的实现细节有一定认识才能避免错误。如下对比相对傻瓜一些的包装类实现(只需关心 原有类提供的接口与输出,不会关注实现细节)
import java.util.Collection;
import java.util.HashSet;
public class WrapperHashSet<E> {
private HashSet<E> hashSet;
private int acount = 0;
public WrapperHashSet(HashSet<E> hashSet) {
this.hashSet = hashSet;
}
public void add(E e) {
acount++;
hashSet.add(e);
}
public void addAll(Collection<? extends E> list) {
acount += list.size();
hashSet.addAll(list);
}
}
如上WrapperHashSet 在 实现add, addAll分别用hashSet来调用实现,自身并不参与细节实现。