JDK为程序员提供了大量的类库而为了保持类库的可重用性可扩展性和灵活性其中使用到了大量的设计模式本文将介绍JDK的I/O包中使用到的Decorator模式并运用此模式实现一个新的输出流类
Decorator模式简介
Decorator模式又名包装器(Wrapper)它的主要用途在于给一个对象动态的添加一些额外的职责与生成子类相比它更具有灵活性
有时候我们需要为一个对象而不是整个类添加一些新的功能比如给一个文本区添加一个滚动条的功能我们可以使用继承机制来实现这一功能但是这种方法不够灵活我们无法控制文本区加滚动条的方式和时机而且当文本区需要添加更多的功能时比如边框等需要创建新的类而当需要组合使用这些功能时无疑将会引起类的爆炸
我们可以使用一种更为灵活的方法就是把文本区嵌入到滚动条中而这个滚动条的类就相当于对文本区的一个装饰这个装饰(滚动条)必须与被装饰的组件(文本区)继承自同一个接口这样用户就不必关心装饰的实现因为这对他们来说是透明的装饰会将用户的请求转发给相应的组件(即调用相关的方法)并可能在转发的前后做一些额外的动作(如添加滚动条)通过这种方法我们可以根据组合对文本区嵌套不同的装饰从而添加任意多的功能这种动态的对对象添加功能的方法不会引起类的爆炸也具有了更多的灵活性
以上的方法就是Decorator模式它通过给对象添加装饰来动态的添加新的功能如下是Decorator模式的UML图
Component为组件和装饰的公共父类它定义了子类必须实现的方法
ConcreteComponent是一个具体的组件类可以通过给它添加装饰来增加新的功能
Decorator是所有装饰的公共父类它定义了所有装饰必须实现的方法同时它还保存了一个对于Component的引用以便将用户的请求转发给Component并可能在转发请求前后执行一些附加的动作
ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰可以使用它们来装饰具体的Component
Java IO包中的Decorator模式
JDK提供的javaio包中使用了Decorator模式来实现对各种输入输出流的封装以下将以javaioOutputStream及其子类为例讨论一下Decorator模式在IO中的使用
首先来看一段用来创建IO流的代码
以下是代码片段
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
testtxt));
} catch (FileNotFoundException e) {
eprintStackTrace();
}
这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了我们使用DataOutputStream封装了一个FileOutputStream这是一个典型的Decorator模式的使用FileOutputStream相当于ComponentDataOutputStream就是一个Decorator将代码改成如下将会更容易理解
以下是代码片段
try {
OutputStream out = new FileOutputStream(testtxt);
out = new DataOutputStream(out);
} catch(FileNotFoundException e) {
eprintStatckTrace();
}
由于FileOutputStream和DataOutputStream有公共的父类OutputStream因此对对象的装饰对于用户来说几乎是透明的下面就来看看OutputStream及其子类是如何构成Decorator模式的
OutputStream是一个抽象类它是所有输出流的公共父类其源代码如下
以下是代码片段
public abstract class OutputStream implements Closeable Flushable {
public abstract void write(int b) throws IOException;
}
它定义了write(int b)的抽象方法这相当于Decorator模式中的Component类
ByteArrayOutputStreamFileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承以ByteArrayOutputStream为例
以下是代码片段
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this();
}
public ByteArrayOutputStream(int size) {
if (size 〈 ) {
throw new IllegalArgumentException(Negative initial size: + size);
}
buf = new byte[size];
}
public synchronized void write(int b) {
int newcount = count + ;
if (newcount 〉 buflength) {
byte newbuf[] = new byte[Mathmax(buflength 〈〈 newcount)];
Systemarraycopy(buf newbuf count);
buf = newbuf;
}
buf[count] = (byte)b;
count = newcount;
}
}
它实现了OutputStream中的write(int b)方法因此我们可以用来创建输出流的对象并完成特定格式的输出它相当于Decorator模式中的ConcreteComponent类
接着来看一下FilterOutputStream代码如下
以下是代码片段
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
thisout = out;
}
public void write(int b) throws IOException {
outwrite(b);
}
}
同样它也是从OutputStream继承但是它的构造函数很特别需要传递一个OutputStream的引用给它并且它将保存对此对象的引用而如果没有具体的OutputStream对象存在我们将无法创建FilterOutputStream由于out既可以是指向FilterOutputStream类型的引用也可以是指向ByteArrayOutputStream等具体输出流类的引用因此使用多层嵌套的方式我们可以为ByteArrayOutputStream添加多种装饰这个FilterOutputStream类相当于Decorator模式中的Decorator类它的write(int b)方法只是简单的调用了传入的流的write(int b)方法而没有做更多的处理因此它本质上没有对流进行装饰所以继承它的子类必须覆盖此方法以达到装饰的目的
BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类它们相当于Decorator模式中的ConcreteDecorator并对传入的输出流做了不同的装饰以BufferedOutputStream类为例
以下是代码片段
public class BufferedOutputStream extends FilterOutputStream {
private void flushBuffer() throws IOException {
if (count 〉 ) {
outwrite(buf count);
count = ;
}
}
public synchronized void write(int b) throws IOException {
if (count 〉= buflength) {
flushBuffer();
}
buf[count++] = (byte)b;
}
}
这个类提供了一个缓存机制等到缓存的容量达到一定的字节数时才写入输出流首先它继承了FilterOutputStream并且覆盖了父类的write(int b)方法在调用输出流写出数据前都会检查缓存是否已满如果未满则不写这样就实现了对输出流对象动态的添加新功能的目的
下面将使用Decorator模式为IO写一个新的输出流
自己写一个新的输出流
了解了OutputStream及其子类的结构原理后我们可以写一个新的输出流来添加新的功能这部分中将给出一个新的输出流的例子它将过滤待输出语句中的空格符号比如需要输出java io OutputStream则过滤后的输出为javaioOutputStream以下为SkipSpaceOutputStream类的代码
以下是代码片段
import javaioFilterOutputStream;
import javaioIOException;
import javaioOutputStream;
/**
* A new output stream which will check the space character
* and wont write it to the output stream
* @author Magic
*
*/
public class SkipSpaceOutputStream extends FilterOutputStream {
public SkipSpaceOutputStream(OutputStream out) {
super(out);
}
/**
* Rewrite the method in the parent class and
* skip the space character
*/
public void write(int b) throws IOException{
if(b!= ){
superwrite(b);
}
}
}
它从FilterOutputStream继承并且重写了它的write(int b)方法在write(int b)方法中首先对输入字符进行了检查如果不是空格则输出
以下是一个测试程序
以下是代码片段
import javaioBufferedInputStream;
import javaioDataInputStream;
import javaioDataOutputStream;
import javaioIOException;
import javaioInputStream;
import javaioOutputStream;
/**
* Test the SkipSpaceOutputStream
* @author Magic
*
*/
public class Test {
public static void main(String[] args){
byte[] buffer = new byte[];
/**
* Create input stream from the standard input
*/
InputStream in = new BufferedInputStream(new DataInputStream(Systemin));
/**
* write to the standard output
*/
OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(Systemout));
try {
Systemoutprintln(Please input your words: );
int n = inread(bufferbufferlength);
for(int i=;i〈n;i++){
outwrite(buffer[i]);
}
} catch (IOException e) {
eprintStackTrace();
}
}
}
执行以上测试程序将要求用户在console窗口中输入信息程序将过滤掉信息中的空格并将最后的结果输出到console窗口比如
以下是引用片段
Please input your words:
a b c d e f
abcdef
总 结
在javaio包中不仅OutputStream用到了Decorator设计模式InputStreamReaderWriter等都用到了此模式而作为一个灵活的可扩展的类库JDK中使用了大量的设计模式比如在Swing包中的MVC模式RMI中的Proxy模式等等对于JDK中模式的研究不仅能加深对于模式的理解而且还有利于更透彻的了解类库的结构和组成