IO流所涉及到的Decorator设计模式

前段时间的时候总结了一篇关于IO流所用到的Decorator设计模式的文章,今天再看一遍,感觉收获些小许,嘿嘿:
首先看下面两条语句:
InputStream  input = new BufferedInputStream(new FileInputStream(“1.txt”)) 
InputStream input= new DataIputStream(new FileInputStream(“1.txt”))

以前就见过好多这种结构,把字节封装成字符串等等其他的形式,或者给读取文件增加一个缓冲区,这种输入输出流有很多种组合,那么他们是遵循那一种规律的呢?原来这都是Decorator的功劳,Decorator,装饰模式,又名Wrapper,此模式的目的在于动态扩展一个类的功能。
当我们要拓展一个类的功能,我们很自然的想到从这个类派生出一个子类,附加一些方法,就实现了类的拓展,事实上这正是面向对象的威力:复用基类的代码,并且扩展出功能更强大的子类。
但是这种扩展时静态的,在编译的时候已经确定了子类的功能,试想一下我们需要的输入功能:文件输入,原始类型数据输入,缓冲输入,字符串输入。。。如果同时需要文件输入和缓冲输入的功能,就必须再次派生一个新类,这样组合下来,子类的数量会相当可观。
这种静态静态组合继承严重影响了可扩展性,试想,某一天需要加入一个新的输入流,比如ZipInputStream,配合其他的功能又需要写一系列的子类,这不仅仅是需要劳动力的问题和时间,而且他是不可以动态的加载进去。
那么怎么解决由于不同功能的组合将派生出大量的子类呢,Decorator模式实现了动态扩展,即在运行时动态地给一个类“装饰”上某些功能。
同时它是给对象的装饰,不是给类的装饰,也就是说没加修饰之前他们两的没有什么关系,如果说有关系的话,那就是共同继承一个父类吧,这一点可以参考动态加载的class.forname(),class生成之后因为class.forname()才把他加载进去,但静态加载在初始化的时候就已经确定了对象之间的关系了。
比如一个FileInputStream 的实例已经生成,现在要给它“装上”缓冲的功能,就把它“装饰”成BufferedInputStream,这样这个FileInputStream就动态具备了缓冲功能。在java中,可以用下面的代码:
InputStream inp = new BufferedInputStream(new FileInputStream());

而这个缓冲功能是在程序运行时动态加上去的。
Java的IO包共有4类重要的输出流:InputStream,OutputStream,Reader,Writer。InputStream和Reader类似,但Reader可以正确转换字符编码,同理,OutputStream和Writer类似;
[img]http://dl.iteye.com/upload/attachment/350944/56c34d96-1dbf-3ba3-b826-3044c6b3ac0a.png[/img]
根据今天看的IO内部部分代码,现在写出一个山寨版的Decorator模式的IO:
一、编写一个Work类,这个类相当于IO里面的龙头老大InputStream,是一个抽象类,代码如下:
package oop.kanglong.testIO2;

public abstract class Work {

public abstract void paint();

}

二 、编写一个Parent类,这个类相当于IO里面的FilterInputStream,这个类继承了Work类,代码如下:
package oop.kanglong.testIO2;

public class Parent extends Work {
protected Work work;
protected Parent(Work work) {
this.work = work;
}
public void paint() {
work.paint();
}
}

三,编写一个Son类,这个类就相当于IO里面的FileInputStream,这个类继承了Work类,代码如下:
package oop.kanglong.testIO2;
public class Son extends Work {
public void paint() {
System.out.println("儿子正在写作业!!!");
}
}

四、编写一个修饰Son的修饰Mother类,这个类相当于IO里面的BufferedInputStream,这个类继承了Parent类,代码如下:
package oop.kanglong.testIO2;

public class Mother extends Parent {
protected Mother(Work work) {
super(work);
}
public void paint() {
System.out.println("妈妈在厨房做菜!!!");
super.paint();
System.out.println("妈妈已经做完菜啦!!!快过来吃饭喽!!!");
}}

五、还可以编写一个修饰Father类,当然修饰类可以任意多个,这个类相当于IO里面的DataInputStream,这个类继承了Parent类,代码如下:
package oop.kanglong.testIO2;
public class Father extends Parent {
protected Father(Work work) {
super(work);
}
public void paint() {
System.out.println("爸爸在客厅看报纸!!!");
work.paint();
System.out.println("爸爸看完报纸准备吃饭啦!!!");
}
}

最后一个就是测试类了:
package oop.kanglong.testIO2;

public class DecoratorTest {
public static void main(String[] args) {
Work work = new Son();
work.paint();
System.out.println("");
work = new Mother(work);
work.paint();
System.out.println("");
work = new Father(work);
work.paint();
}
}

测试结果为:
儿子正在写作业.

妈妈在厨房做菜。
儿子正在写作业。
妈妈已经做完菜啦。快过来吃饭喽。

爸爸在客厅看报纸。
妈妈在厨房做菜。
儿子正在写作业。
妈妈已经做完菜啦。快过来吃饭喽。
爸爸看完报纸准备吃饭啦。

可以看到,这是一级一级的修饰下去的!!!

[b]小结:[/b]
以刚刚那个Demo为例:
我们创建一个Mother对象并且传入一个Son对象的参数,在初始化的时候,Mother对象的构造函数调用父类的构造方法,其实就是给Work赋一个值:Work work = son;当我们调用Mother的paint方法时,原方法等价于下面方法:
public void paint() {
System.out.println("妈妈在厨房做菜!!!");
son.paint();//super.paint();
System.out.println("妈妈已经做完菜啦!!!快过来吃饭喽!!!");
}

那就是说可以给原有的son.paint();根据不同的需要增加不同的方法。
现在不难理解下面两条语句了:
work = new Mother(work);
InputStream input = new BufferedInputStream(new FileInputStream(“1.txt”)) ;


关于Decorator还有其他的一些理解:
Decorator模式和Proxy模式的区别:
Decorator和Proxy非常相似,Decorator类和Proxy类都包含一个对象的引用,但是他们有各自不同的目标:Decorator是为了给一个对象动态加上某些功能,而Porxy是为了控制对象的访问权限,而且,Decorator必定保持一个被装饰对象的引用,但是Proxy却不一定,因为Proxy是为了检查权限,它可能不直接引用一个对象,而是直接访问一个对象,比如通过InetAddress访问一台远程计算机。

Decorator模式和Adapter模式的区别:
两者都可以动态改变对象的行为,但是Decorator不会改变对象的接口,比如用BufferedInputStream装饰一个FileInputStream后,它还是一个InputStream,而Adapter不同,他完全改变了对象的原有接口,比如前面提到的用InputStreamReader适配一个FileInputStream后,它就不是InputStream而变成Reader了。
[img]http://dl.iteye.com/upload/attachment/350942/6b6c360c-459f-3f9d-a766-0d6cb494936a.png[/img]
我们可以看到,InputStreamReader不在继承InputStream这个抽象类了。。。

IO流还使用了透明和不透明的Decorator模式:
所谓透明的Decorator模式是指,使用Decorator装饰后,客户端看不出这个对象和原来的对象有什么区别,比如:
InputStream input = new FileInputStream(“1.txt”);
InputSteam input = new BufferedInputStream(new FileInputstream(“2.txt”));

他们得到的两个input都是InputStream,客户端不能区分它的实际类型,这符合针对抽象编程的思想,可以使代码改动的最小。
String里面的“控制反转”也是用到这种抽象编程的思想:
注入一个借口到一个action里面,则只需要在applicationcontext里面配置即可,注入的接口对象可以根据不同的实现类,实现不同的功能,这样以后要想更改某种接口的实现的时候,就可以直接在配置文件中更改即可,这样大大降低了类与类之间的耦合度了。。

当然是用不透明的Decorator模式时,客户端就能明确知道对象的实际类型,比如:
BufferedInputStream input = new BufferedInputStream(new FileInputStream(“2.txt”));

客户端知道要得到的input是一个BufferedInputStream,这样客户端就可以是用一些BufferedInputStream提供的额外的方法,比如readLine(),有时就是为了要使用这些附加功能,这是就用不透明的装饰模式。

最后一个就是Decorator模式的应用了:
IO流式用的最多的,不过就现在看来,这种思想在Spring里面也用到了不少啊。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值