通俗的讲装饰器就是在不改变对象结构的前提下,扩充对象的功能。
下面以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 }
总结 其实装饰器基类中有一个变量保存被装饰器类对象,装饰器和被装饰器的功能扩展是基于两者都实现相同的接口,即类型相同,然后可以互相发送消息,扩展功能。