通俗的讲装饰器就是在不改变对象结构的前提下,扩充对象的功能。
下面以effective java中例题
问题 我想扩充Hash<set>功能,使得能够统计添加过元素的个数?
或许你可能会自定义一个类通过继承扩展,从而实现计数功能,代码如下:
package com.effectJava.Chapter2;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount=0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(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;
}
public static void main(String... args) {
InstrumentedHashSet s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("shape", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
上面代码直接继承hashSet类,然后覆盖 add和addAll方法 ,你会发现最终结果不是3,而是6,其实addAll内部实现是通过调用add,可能你又想到可以通过删除addAll方法上的 标志删除的那行代码,通过上述操作确实能够实现功能,但是这种功能的实现的子类比较脆弱,如果父类增加新方法,或者原方法有改动,都可能导致统计失败。
为此我们提出装饰器设计模式, 通过被装饰者和装饰者之间的互相调用来实现扩展技术功能的目的。
首先定义一个装饰器基类ForwardingSet<E> 实现Set<E>接口,由于add和addAll方法都在Set接口中定义,因此可以通过实现此接口定义装饰器
package com.effectJava.Chapter2;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
//装饰器
public class ForwardingSet<E> implements Set<E> {
private final Set<E> set;
public ForwardingSet(Set<E> set) {
this.set = set;
}
@Override
public int size() {
return set.size();
}
@Override
public boolean isEmpty() {
return set.isEmpty();
}
@Override
public boolean contains(Object o) {
return set.contains(o);
}
@Override
public Iterator<E> iterator() {
return set.iterator();
}
@Override
public Object[] toArray() {
return set.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return set.toArray(a);
}
@Override
public boolean add(E e) {
return set.add(e);
}
@Override
public boolean remove(Object o) {
return set.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return set.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
@Override
public void clear() {
set.clear();
}
}
定义一个装饰器类
package com.effectJava.Chapter2;
import java.util.*;
//具体的装饰器类
//装饰对象可以在转发这些请求以前或以后增加一些附加功能,这样就可以确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。
//在面向对象的设计中,通常通过继承来事项对给定类的功能扩展。
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int CountSize=0;
public InstrumentedSet(Set<E> set) {
super(set);
}
@Override
public boolean addAll(Collection<? extends E> c) {
CountSize += c.size();
return super.addAll(c);
}
@Override
public boolean add(E e) {
CountSize++;
return super.add(e);
}
public int getCountSize() {
return CountSize;
}
}
定义一个被装饰者(这里我们直接用HashSet因为它实现类Set<E>,避免我们自己取实现)
public static void main(String... args) {
// HashSet是被装饰者, InstrumentedSet是装饰者
InstrumentedSet<String> s = new InstrumentedSet<String>(new HashSet<String>());
s.addAll(Arrays.asList("1", "2", "3"));
System.out.println(s.getCountSize());//结果为3
}
总结 其实装饰器基类中有一个变量保存被装饰器类对象,装饰器和被装饰器的功能扩展是基于两者都实现相同的接口,即类型相同,然后可以互相发送消息,扩展功能。