设计模式------装饰者设计模式(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流的应用)的主要内容,如果未能解决你的问题,请参考以下文章