设计模式------装饰者设计模式(IO流的应用)

Posted qingruihappy

tags:

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

一:io流中的装饰者设计模式

java.io包内的类太多了,简直是……"排山倒海"。你第一次(还有第二次和第三次)看到这些API发出"哇"的惊叹时,放心,你不是唯一受到惊吓的人。现在,你已经知道装饰者模式,这些I/O的相关类对你来说应该更有意义了,因为其中许多类都是装饰者。下面是一个典型的对象集合,用装饰者来将功能结合起来,以读取文件数据:

技术分享图片

我们来看一下下面的这张类图

技术分享图片

你可以发现,和星巴兹的设计相比,java.io其实没有多大的差异。我们把java.ioAAPI范围缩小,让你容易查看它的文件,并组合各种"输入"流装饰者来符合你的用途。

你会发现"输出"流的设计方式也是一样的。你可能还会发现Reader/Writer流(作为基于字符数据的输入输出)和输入流/输出流的类相当类似(虽然有一些小差异和不一致之处,但是相当雷同,所以你应该可以了解这些类)。

但是JavaAI/O也引出装饰者模式的一个"缺点":利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。但是,现在你已经了解了装饰者的工作原理,以后当使用别人的大量装饰的API时,就可以很容易地辨别出他们的装饰者类是如何组织的,以方便用包装方式取得想要的行为。

1.1,下面我们结合着BufferedInputStream和 BufferedOutputStream来分析一下装饰着设计模式代码很简单,主要看得就是它的源码

public class Test {

    public static void copyMp3() {

         try {

              FileInputStream fi = new FileInputStream("audio.mp3");

              BufferedInputStream buf = new BufferedInputStream(fi);

              FileOutputStream fio = new FileOutputStream("audioCapy.mp3");

              BufferedOutputStream buo = new BufferedOutputStream(fio);

              int ch = 0;

              while ((ch = buf.read()) != -1) {

                   buo.write(ch);

              }

              buf.close();

              buo.close();

         } catch (FileNotFoundException e) {

              e.printStackTrace();          } catch (IOException e) {

              e.printStackTrace();

         }

    }

}

BufferedInputStream buf = new BufferedInputStream(new FileInputStream("audio.mp3"));

我们来看一下这行代码

private static int defaultBufferSize = 8192;

public BufferedInputStream(InputStream in) {

this(in, defaultBufferSize);

}

初始化的时候就会进来这个方法中。

在进一层可以看到这行代码,意思就是把buf的数组长度复制为8192

public BufferedInputStream(InputStream in, int size) {

super(in);

if (size <= 0) {

throw new IllegalArgumentException("Buffer size <= 0");

}

buf = new byte[size];

}

下面我们来看这行代码。

buf.read()

public synchronized int read() throws IOException {

if (pos >= count) {

fill();

if (pos >= count)

return -1;

}

return getBufIfOpen()[pos++] & 0xff;

}

private byte[] getBufIfOpen() throws IOException {

byte[] buffer = buf;

if (buffer == null)

throw new IOException("Stream closed");

return buffer;

}

实际上返回的getBufIfOpen就是我们上面构造方法中的初始化的数组8192 下面我们来看fill()的方法

private void fill() throws IOException {

byte[] buffer = getBufIfOpen();

if (markpos < 0)

pos = 0; /* no mark: throw away the buffer */

else if (pos >= buffer.length) /* no room left in buffer */

if (markpos > 0) { /* can throw away early part of the buffer */

int sz = pos - markpos;

System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz;

markpos = 0;

} else if (buffer.length >= marklimit) {

markpos = -1; /* buffer got too big, invalidate mark */

pos = 0; /* drop buffer contents */

} else { /* grow buffer */

int nsz = pos * 2;

if (nsz > marklimit)

nsz = marklimit;

byte nbuf[] = new byte[nsz];

System.arraycopy(buffer, 0, nbuf, 0, pos);

if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {

// Can‘t replace buf if there was an async close.

// Note: This would need to be changed if fill()

// is ever made accessible to multiple threads.

// But for now, the only way CAS can fail is via close.

// assert buf == null;

throw new IOException("Stream closed");

}

buffer = nbuf;

}

count = pos;

int n = getInIfOpen().read(buffer, pos, buffer.length - pos);

if (n > 0)

count = n + pos;

}

我们来看getInIfOpen().read(buffer, pos, buffer.length - pos)这行代码,具体的逻辑实现我们先不管,主要就是研究装饰者设计模式的。

private InputStream getInIfOpen() throws IOException { InputStream input = in;

if (input == null)

throw new IOException("Stream closed"); return input;

}

在这里我们就确定了getInIfOpen是要返回的就是BufferedInputStream buf = new

BufferedInputStream(new FileInputStream("audio.mp3"));中的FileInputStream,现在我们去 fileinputstream中来找read(buffer, pos, buffer.length - pos)方法。

protected volatile InputStream in;

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

}

这个时候我们就可以看到这里的in就是inputStream最里面的核心的要被装饰的类。

现在我们在到inputStream的源码中来可以看到就是这样的。

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;

}

从上面的一整套的逻辑我们可以看到,BufferedInputStream的read方法扩展了核心的类 inputStream的功能,这个就是典型的装饰着设计模式,其实io流中有都是基于

InputStream的装饰着模式的扩张的应用的。

1.2,下面我们自己来定义一个流的类基于BufferedInputStream的在装饰

技术分享图片

就是把首字符变成小写的。

LowerCaseInputStream.java

package com.DesignPatterns.ac.decorator_io; import java.io.*; public class LowerCaseInputStream extends FilterInputStream {

    public LowerCaseInputStream(InputStream in) {

         super(in);

    }

    public int read() throws IOException {

         int c = in.read();

         return (c == -1 ? c : Character.toLowerCase((char) c));

    }

    public int read(byte[] b, int offset, int len) throws IOException {

         int result = in.read(b, offset, len);

         for (int i = offset; i < offset + result; i++) {

              b[i] = (byte) Character.toLowerCase((char) b[i]);

         }

         return result;

    }

}

Test

package com.DesignPatterns.ac.decorator_io; import java.io.*;

public class InputTest {

    public static void main(String[] args) throws IOException {

         int c;

         try {

              InputStream in = new LowerCaseInputStream(new

BufferedInputStream(new FileInputStream("test.txt")));

              while ((c = in.read()) >= 0) {

                   System.out.print((char) c);

              }

              in.close();

         } catch (IOException e) {

              e.printStackTrace();

         }

    }

}

in.read(b, offset, len)这一行代码的装饰着设计模式我们上面说过了,就不在说了,那我们就主要的来看这里的再次装饰的逻辑

for (int i = offset; i < offset + result; i++) {

              b[i] = (byte) Character.toLowerCase((char) b[i]);

         }

当经过一次装饰之后我们再次对输出的结果进行修改,相当于又套了一个马甲又多了一个功能。这个就是典型的装饰着设计模式。 fff

以上是关于设计模式------装饰者设计模式(IO流的应用)的主要内容,如果未能解决你的问题,请参考以下文章

IO流与装饰者模式

装饰模式笔记

IO流的设计模式

装饰者设计模式

Java IO 装饰者模式

Java实现装饰者模式