谈谈对java I/O中装饰者模式的理解

看字面意思,装饰者就是把一个对象装饰一下,那么必要要有一个装饰着和被装饰着,被装饰者是比较原始的东西,比如一个原始的木门,大家都涂成各种颜色什么的,这里木门就是个被装饰者,各种颜料就是装饰着.装饰者要装饰被装饰者,必然要拥有一个被装饰着的对象,现在来看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
从中可以看出FilterInputStream是跟其他的FileInputStream等平行的,都是继承InputStream,FilterInputStream的子类都是装饰者类。


你可能感兴趣的:(谈谈对java I/O中装饰者模式的理解)