看字面意思,装饰者就是把一个对象装饰一下,那么必要要有一个装饰着和被装饰着,被装饰者是比较原始的东西,比如一个原始的木门,大家都涂成各种颜色什么的,这里木门就是个被装饰者,各种颜料就是装饰着.装饰者要装饰被装饰者,必然要拥有一个被装饰着的对象,现在来看java I/O,I/O的本质是从文件,网络等地方读取字节流,FileInputStream是从文件中读取字节流,很原始了,它就是一个被装饰者,其他还有StringBufferInputStream,ByteArrayInputStream等,装饰着就是BufferdInputStream,猜测BufferdInputStream必然会有一个被装饰着的对象,它提供的功能是缓冲区,它可以给前面所有的被装饰者提供缓冲功能。先看headFirst设计模式一书给出的例子,比较好懂,现在假设FileInputStream已经读出字节了,要把读出来的字节中小写字母全部转化为大写字母,就是把FileInputStream装饰一下,设计一个这样的类:
public class LowerCaseInputStream extends FilterInputStream {
protected LowerCaseInputStream(InputStream in) {
super(in);//调用父类FilterInputStream,父类中持有一个被装饰者InputStream的对象
}
@Override
public int read() throws IOException {
int c = super.read();
return (c == -1)?c:Character.toLowerCase((char)c);
}
}
看LowerCaseInputStream,read方法相当于真正的装饰方法,要实现小写转大写的,如果传入一个FileInputStream,就持有了被装饰者对象,可以肆意的装饰它了,看怎么使用的:
String path = new String(System.getProperty("user.dir")+File.separator+"file/test.txt");
try {
InputStream is = new FileInputStream(path);//没有使用装饰者的时候
int c;
while((c=is.read())>0){
System.out.print((char)c);
}
System.out.println();
System.out.println("使用装饰者模式输出:");
InputStream myis = new LowerCaseInputStream(new FileInputStream(path));
while((c=myis.read())>0){
System.out.print((char)c);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
输出:
bCd
使用装饰者模式:
bcd
很简单,一个自己的装饰者就做好了,当然你还可以有其他装饰着,如BufferdInputStream等,像下面使用:
InputStream myis = new BufferedInputStream(new LowerCaseInputStream(new FileInputStream(path)));
这样就有两个装饰着同时修饰FileInputStream了。很难理解为什么要这么用,直接用FileInputStream读出来然后再转大小写不行吗?可以这样来理解,木门为什么不直接在生产的时候就加个搞的比较好看呢?,因为木门需求还没有完全确定,每个用户需要的颜色都不一样,也就是说木门只提供最基本的功能,看FileInputStream类,它也只提供最基本的功能,如读写字节什么的,但是至于什么缓冲功能,大小写转换就需要等程序员想用的时候自己来选择,在java I/O中,FileInputStream和BufferedInputStream都继承InputStream,我的理解是:会有很多装饰者和很多被装饰者,但是装饰者要持有被装饰者的对象,假设FileInputStream等被装饰者都继承了InputStream了,这样只要传入一个InputStream进去装饰着BufferedInputStream,它就能持有所有被装饰的对象了,那么BufferedInputStream为什么还有继承InputStream呢?因为像这样的代码:
new LowerCaseInputStream(new FileInputStream(path),把被装饰者装饰了之后返回的对象是什么呢?必要还要返回被装饰者的对象,你不能把门涂层颜色就不是门了吧?所以装饰着也继承于InputStream,但是它不是直接继承InputStream,而是继承FilterInputStream,但是FilterInputStream继承InputStream,有一层FilterInputStream间接来继承InputStream,而继承与FilterInputStream的所有子类都是装饰者类,在FilterInputStream中持有一个被装饰者类的对象,这样子类就不用持有了被装饰者对象了,都从父类FilterInputStream继承过来了。最后看一下java I/O的BufferedInputStream源代码:
public BufferedInputStream(InputStream in) {
this(in, defaultBufferSize);
}
public synchronized int read() throws IOException {
if (pos >= count) {
fill();//这里面会调用被装饰者的read方法
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
其实它跟LowerCaseInputStream是一样的,构造函数接受一个InputStream的被装饰者,read函数是具体的装饰功能。最后上个java I/O的包图结构
- java.io.InputStream (implements java.io.Closeable)
- java.io.ByteArrayInputStream
- java.io.FileInputStream
- java.io.FilterInputStream
- java.io.BufferedInputStream
- java.io.DataInputStream (implements java.io.DataInput)
- java.io.LineNumberInputStream
- java.io.PushbackInputStream
- java.io.ObjectInputStream (implements java.io.ObjectInput, java.io.ObjectStreamConstants)
- java.io.PipedInputStream
- java.io.SequenceInputStream
- java.io.StringBufferInputStream