装饰器设计模式

Posted 夜空中最亮的星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器设计模式相关的知识,希望对你有一定的参考价值。

通俗的讲装饰器就是在不改变对象结构的前提下,扩充对象的功能。

    下面以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
    }

  总结   其实装饰器基类中有一个变量保存被装饰器类对象,装饰器和被装饰器的功能扩展是基于两者都实现相同的接口,即类型相同,然后可以互相发送消息,扩展功能。

 

 

 

      

以上是关于装饰器设计模式的主要内容,如果未能解决你的问题,请参考以下文章

编程模式之Go语言如何实现装饰器

设计模式之装饰器模式

装饰器

学习设计模式之装饰器模式

装饰器模式问题 - 如何调用嵌套装饰器方法?

python装饰器