装饰者设计模式就是在不改动原类文件和使用继承的情况下,通过创建一个新的对象来对原对象进行包装,以此来增强原对象的一些功能。现在我们通过对java IO中的字节字符流以及其对应的高效缓冲流来学习一下装饰者设计模式。
1.字节流FileInputStream、FileOutputStream与BufferedInputStream 、BufferedOutputStream
public static void copy()throws IOException{
FileInputStream fis = new FileInputStream("demo.txt");
FileOutputStream fos = new FileOutputStream("demo_copy.txt");
int ch = 0;
while((ch= fis.read()) != -1){
fos.write(ch);
}
fis.close();
fos.close();
}
看上面一段代码,FileInputStream每次只读取一个字节然后通过FileOutputStream每次写入一个字节到文件中。这就好比现在A地有一大堆西瓜,然后我现在用一辆货车把这些西瓜运到B地去,但是每次我只运一个西瓜,天哪,我得运到什么时候去,是不是很浪费效率。现在我们要通过增强read()和write(),我一次从硬盘上读取一定的数据,然后一次性把这些数据在写到硬盘的另一块区域,来提高效率。这样就产生了带有缓冲区的输入输出流BufferedInputStream , BufferedOutputStream。它们内部定义了一个缓冲数组,在读取的时候,先读取一批数据到缓冲数组中,然后从数组中在一个一个读出去,写的时候也是先写到缓冲数组中,然后当缓冲数组满了以后,一次性写到硬盘上。下面是BufferedInputStream,BufferedOutputStream的简单实现。
/**
* 自定义带有缓冲区的高效输出流
*
*/
public class MyBufferedOutputStream extends OutputStream implements Closeable{
private byte[] buf;
private int count;
private OutputStream out;
public MyBufferedOutputStream(OutputStream out){
this(out, 1024 * 4);
}
public MyBufferedOutputStream(OutputStream out , int bufSize){
this.out = out;
this.buf = new byte[bufSize];
}
@Override
public void close() throws IOException {
out.close();
}
@Override
public void write(int b) throws IOException {
if(count >= buf.length){ //如果缓冲数组写满了,那么就写入文件,否则先直接写入缓冲数组中
flush();
}
buf[count++] = (byte)b;
}
public void flush()throws IOException{
if(count>0){
out.write(buf , 0 , count);
count=0;
}
}
}
/**
* 自定义带有缓冲区的高效输入流
*
*/
public class MyBufferedInputStream extends InputStream implements Closeable{
private InputStream in;
private byte[] buff;
private int count = 0;
private int pos = 0;
public MyBufferedInputStream(InputStream in){
this(in,1024*4);
}
public MyBufferedInputStream(InputStream in , int bufSize){
this.in = in;
this.buff = new byte[bufSize];
}
public int read() throws IOException{
if(count == 0){
count = in.read(buff); // 先读一批输入到缓冲数组中,
pos = 0;
}
if(count > 0){
int ch = buff[pos]; // 一个一个再从缓冲数组中读,
count--;
pos++;
return ch & 0xff;
}
return -1;
}
public void close()throws IOException{
in.close();
}
}
/**
* 复制文件
* @param srcFileName 源文件全路径名
* @param desFileName 目的文件全路径名
* @throws IOException
*/
public static void copyFile(String srcFileName , String desFileName) throws IOException{
MyBufferedOutputStream mos= new MyBufferedOutputStream(new FileOutputStream("xiaolu_copy_2.jpg"));
MyBufferedInputStream mis = new MyBufferedInputStream(new FileInputStream("xiaolu.jpg"));
int ch = 0;
while((ch=mis.read()) != -1){
mos.write(ch);
}
close(mos);
close(mis);
}
2.FileReader,FileWriter与BufferedReader,BufferedWriter
我们知道FileReader,FileWriter可以方便的操作文本文件,它们有read()和write()方法,一次读取一个字符,一次往文件中写入一个字符。但是我现在想要一次读取一行,一次往文件中写入一行的功能。我们现在分析一下:一次读取一行,其实也是一个字符一个字符的读,当读取到换行符的时候,我们把这些字符一次全部返回回来就行了;一次写一行也一样,最终也是调用写一个字符的方法。这也是对Reader,Writer这两个对象的read,write方法进行增强。下面就是一个简单的实现:
/**
*自定义Reader包装类,添加读取一行的方法
*
*/
private class MyBufferedReader extends Reader implements Closeable{
private Reader r;
public MyBufferedReader(Reader r){
this.r = r;
}
/**
* 读取一行, 注意:该方法只是用window平台
* @return
* @throws IOException
*/
public String readLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read()) != -1){
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length() != 0)
return sb.toString();
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return r.read(cbuf, off, len);
}
public void close()throws IOException{
r.close();
}
}
/**
*自定义Writer包装类,添加写字符串,换行功能
*
*/
public class MyBufferedWriter extends Writer implements Closeable{
private Writer w;
public MyBufferedWriter(Writer w){
this.w = w;
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
w.write(cbuf, off, len);
}
/**
* 写一个字符串
*/
public void write(String str)throws IOException{
char[] chs = str.toCharArray();
write(chs,0,chs.length);
}
/**
* 换行
* @throws IOException
*/
public void newLine()throws IOException{
write("\r\n"); // 注意该方法没有考虑跨平台行,仅在windows系统下使用
}
@Override
public void flush() throws IOException {
w.flush();
}
@Override
public void close() throws IOException {
w.close();
}
}
工具方法:
/**
* 关闭资源
* @param obj
*/
public static void close(Closeable obj){
try{
if(obj !=null)
obj.close();
}catch(IOException e){
e.printStackTrace();
}
}
总结:当一个类需要增强功能的时候,当然也可以是减弱功能,我们可以使用装饰者设计模式来设计,使用装饰者设计模式来做,我们不需要更改原来的类,也不需要继承原来的类,比较灵活。使用装饰者模式的时候,装饰类要和原类继承相同的基类,然后在构造的时候传入要增强功能的类,然后在对应方法上对原类的功能进行增强。