从2016.11月工作以来很少有时间和心情认真写几篇博客了,以后还是应该回归自己这个明净的世界。
什么时候忘记适用性了 优缺点了 点它:装饰模式–百科
讲真,这个东西不是说抄一遍就能get的技能,要不重构一下自己的code基本没可能。
先理解一下工作原理
我们现在生活中理解一下Decorator Pattern.外国人都拿咖啡举例子,国内就说要用买煎饼,当然我觉得后者靠谱。快上车,老司机带你卖波煎饼
- 买煎饼这件事这个图基本可以概括了。这段代码建议脑补
上定义
- The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending funcitionality.
- 首先我们应该知道的是 Decorator Pattern 所普遍具有的类图结构大致如下:
源码类图
把应用
Decorator pattern
的java.io
包中相关 和InputStream
的内容捋一下。(当然本包中不只有InputStream使用了 Decorator Pattern)- 巴一眼UML:
以上类图的内在逻辑
小结:在学习设计模式之前我们看到这样的代码,除非有十个八个相同结构的代码供我们查看,否则我们是不能清晰的理出这其中的逻辑的。这也从侧面印证了 “设计模式是思维的复用”
- 巴一眼UML:
从真实代码的角度更深入的理解这一设计(思维)模式。
感觉有点力不从心,这更说明总结记录的重要性,这点知识掌握的还不够完全呢。(废话少说)
上干货:FilterInputStream概括性的介绍,再加上煎饼我们基本把这个事给理解了。
- InputStream
- FileInputStream
关于以上两个图我只是想说用面向接口的思维理解这个抽象方法和这个子类。
当然子类FileInputStream已经具体实现了InputStream中的read()方法,实现方式是通过JNI调用本地接口。这个本地接口的功能就是到物理存储设备上把二进制数据拿到内存中。
class FileInputStream extends InputStream
{
//……
/**
* Reads a byte of data from this input stream. This method blocks if no input is yet available.
*
* @return the next byte of data, or <code>-1</code> if the end of the file is reached.
* @exception IOException if an I/O error occurs.
*/
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
//……
}
就像你看到的read()
每次只能读取一个字节,我们知道从物理设备上读取数据相对于从内存读取要更加耗费时间,所以这里每次读取一个字节使我们不能接受的。当然 I/O 维护者和我们有同样的顾虑。
所以我们也听说了 BufferInputStream 这个带缓冲区的InputStream,它具有InputStream的特性,同时比单纯的InputStream多了一个对缓冲区的维护。[是不是有点 Decorator的意思]
1. BufferInputStream
你或许觉的我说的不对,因为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;
}
但是你要知道如果有人把这个你所谓的“若干”给你封装好,而不用你自己维护,这岂不是很方便?[这个人就是Decorator]
我们来看下BufferedInputStream中对于read() 方法的封装:
class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;//byte[DEFAULT_BUFFER_SIZE]-->8K
//……
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
//……
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
private InputStream getInIfOpen() throws IOException {
InputStream input = in;//这个in使我们使用构造方法获取本类对象的时候得到的一个InputStream类型参数
if (input == null)
throw new IOException("Stream closed");
return input;
}
//……
}
说白了这段代码做的事情就是把InputStream中的内容读取到一个java封装好的数据缓冲区(byte数组)中去,并且返回一个值,如果我们是第一次调用这个read()方法返回值就是InputStream中的数据中的第一个字节数据的16进制形式数字。
我还没搞明白:为什么 [value & 0xff] 是把value转换为16进制的形式
。
参考:
- 《Head First Design Patterns》——以上内容大部分来自这里。
- FilterInputStream概括性的介绍
- 这里有煎饼的栗子蛮不错的
- 装饰模式–百科