装饰器模式

1.装饰器模式是什么

1.百度百科

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

2.维基百科

in object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.[1] The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.[2] The decorator pattern is structurally nearly identical to the chain of responsibility pattern, the difference being that in a chain of responsibility, exactly one of the classes handles the request, while for the decorator, all classes handle the request.

3.lz理解

首先不改变原文件并且不使用继承扩展新的功能.

4.核心角色

抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口,这个类并不是必要的。
具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。

2. 装饰器模式解决了什么问题

解决了给对象动态添加职责而不改变其本身的问题。

功能添加灵活 当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。装饰器模式使用组合方式处理职责增加比继承方式有更多的灵活性。不会造成类的急剧增加。

可以多次装饰 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。

独立变化 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

3.装饰器模式用法

1.简单例子

下面我们就从如何打造一个小船说起,我们要造一艘小船。

抽象构建

//船能飘在水上()
public interface Ship{
	void floatingOnTheWater();
}
//抽象船
public class abstractBoatDecorator implements Ship{

	 protected Boat boat;

	 public abstractBoatDecorator(Boat boat) {
		 super();
		 this.boat = boat;
	 }

	@Override
	public void floatingOnTheWater() {
		boat.floatingOnTheWater();
	}

}

public abstract class abstractBoatDecorator implements Ship{

	 protected Boat boat;

	 public abstractBoatDecorator(Boat boat) {
		 super();
		 this.boat = boat;
	 }

	@Override
	public void floatingOnTheWater() {
		boat.floatingOnTheWater();
	}

	public abstract void addSail();

}


客户端对 调用

public class Customer {

	public static void main(String[] args) {
    Boat boat = new Boat();
    boat.floatingOnTheWater();
    StrengthenTheBoat strengthenTheBoat = new StrengthenTheBoat(boat);
    strengthenTheBoat.floatingOnTheWater();
    strengthenTheBoat.addSail();
	}

}

2.装饰器与I/O

读取一个一个文件变成文件流 FileInputStream(path)

抽象构件(Component)角色

public abstract class InputStream implements Closeable {

   public abstract int read() throws IOException;

   public int read(byte b[]) throws IOException {
      return read(b, 0, b.length);
  }

  public int read(byte b[], int off, int len) throws IOException {
    if (b == null) {
        throw new NullPointerException();
    } else if (off < 0 || len < 0 || len > b.length - off) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return 0;
    }

    int c = read();
    if (c == -1) {
        return -1;
    }
    b[off] = (byte)c;

    int i = 1;
    try {
        for (; i < len ; i++) {
            c = read();
            if (c == -1) {
                break;
            }
            b[off + i] = (byte)c;
        }
    } catch (IOException ee) {
    }
    return i;
  }

  public void close() throws IOException {}

  public synchronized void mark(int readlimit) {}

  public long skip(long n) throws IOException {

    long remaining = n;
    int nr;

    if (n <= 0) {
        return 0;
    }

    int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
    byte[] skipBuffer = new byte[size];
    while (remaining > 0) {
        nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
        if (nr < 0) {
            break;
        }
        remaining -= nr;
    }

    return n - remaining;
  }

  //...

}

java每个int是4个字节,而InputStream的read是面向字节的,也就是每次只能读取1个字节,因此在readInt这个方法中,读取出4个字节,再进行处理成一个int,这里我们不对处理过程进行深究。

对于其他的方法思想大致一样,不过由于对于String类型需要对字符进行编码,对字符的长度进行传递,会复杂一点,这里就不多说了,关键是这个类算是对InputStream的一个封装。

具体构件(Concrete Component)角色

public class FilterInputStream extends InputStream {  

    protected volatile InputStream in;  

    protected FilterInputStream(InputStream in) {  
        this.in = in;  
    }  

    public int read() throws IOException {  
        return in.read();  
    }  

    public int read(byte b[]) throws IOException {  
        return read(b, 0, b.length);  
    }  

    public int read(byte b[], int off, int len) throws IOException {  
        return in.read(b, off, len);  
    }  

    public void close() throws IOException {  
        in.close();  
    }

    public long skip(long n) throws IOException {
       return in.skip(n);
    }

    public int available() throws IOException {
        return in.available();
    }

    public synchronized void mark(int readlimit) {
        in.mark(readlimit);
    }

    public synchronized void reset() throws IOException {
        in.reset();
    }

    public boolean markSupported() {
        return in.markSupported();
    }
}  

其中protected volatile InputStream in,维护了一个内部存储的变量

具体装饰(Concrete Decorator)角色

这个类提供了一个缓存来加速我们从输入流的读取。
由于我们从InputStream中读取数据时,一般都会用到os的io,或者网络io,这都是会耗费大量时间的操作,比如我们现在从文件读取前20个字节,过一会又从文件读取20个字节,这就是两次io,好的,有了BufferedInputStream,就解决这个两次io的问题,这个类在read时,干脆多读一部分数据进来,放在内存里,等你每次操作流的时候,读取的数据直接从内存中就可以拿到,这就减少了io次数,加快我们的io。增加了一个新功能。

public class BufferedInputStream extends FilterInputStream {  
  //....省略部分源码  
  private static int DEFAULT_BUFFER_SIZE = 8192;  

  private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;  

  protected volatile byte buf[];  

  public synchronized int read() throws IOException {  
      if (pos >= count) {  
          fill();  
          if (pos >= count)  
              return -1;  
      }  
      return getBufIfOpen()[pos++] & 0xff;  
  }  
  private void fill() throws IOException {  
      byte[] buffer = getBufIfOpen();  
      if (markpos < 0)  
          pos = 0;            
      else if (pos >= buffer.length)   
          if (markpos > 0) {  
              int sz = pos - markpos;  
              System.arraycopy(buffer, markpos, buffer, 0, sz);  
              pos = sz;  
              markpos = 0;  
          } else if (buffer.length >= marklimit) {  
              markpos = -1;   
              pos = 0;
          } else if (buffer.length >= MAX_BUFFER_SIZE) {  
              throw new OutOfMemoryError("Required array size too large");  
          } else {            /* grow buffer */  
              int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?  
                      pos * 2 : MAX_BUFFER_SIZE;  
              if (nsz > marklimit)  
                  nsz = marklimit;  
              byte nbuf[] = new byte[nsz];  
              System.arraycopy(buffer, 0, nbuf, 0, pos);  
              if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {  
                  throw new IOException("Stream closed");  
              }  
              buffer = nbuf;  
          }  
      count = pos;  
      int n = getInIfOpen().read(buffer, pos, buffer.length - pos);  
      if (n > 0)  
          count = n + pos;  
  }  

}  

另外,这个类图只是装饰器模式的完整结构,但其实里面有很多可以变化的地方。

1,Component接口可以是接口也可以是抽象类,甚至是一个普通的父亲。

2,装饰器的抽象父类Decorator并不是必须的。

4.装饰器模式的问题

对象关系不明晰 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

占用系统资源 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。

5.装饰器模式总结

在以下情况下可以考虑使用装饰模式:

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  2. 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。
引用

http://blog.csdn.net/qq_27802405/article/details/51089502
http://www.cnblogs.com/zuoxiaolong/p/pattern11.html
http://blog.csdn.net/zhao123h/article/details/52826682

posted @ 2018-01-31 17:41  枫飘雪落  阅读(178)  评论(0编辑  收藏  举报