最近我给女朋友买了一款可以更换外壳的手机。现在的外壳是红色的,如果我想用这款手机的时候,会更换成银灰色的外壳。但是我不能随意更换天线或者话筒,因为这些功能模块在手机生产的时候就已经被固定了。
软件中的修饰者(decorator),和手机的外壳一样,封装了一些可以替换的功能。例如下面是一段替换Swing中表模型的代码:
TableSortDecorator sortDecorator = new TableSortDecorator(table.getModel());
table.setModel(sortDecorator);
在这段代码中,程序首先将表模型包装在一个修饰对象中。以后当表对它的模型进行操作的时候,它实际上操作的是排序修饰对象(sortDecorator),该修饰对象在表模型中加入了排序功能,而将其他基本的功能委托给缺省的表模型,在修饰模型中,这个缺省的表模型又被称为真实对象(real subject)。
在Java的编程中,基类和子类的继承关系在编译的时候就被固定了,就像手机的天线和话筒一样。由于继承关系是静态的,开发人员无法在程序运行时改变对象的行为。但是通过修饰者开发人员可以在运行时拼装对象,因此修饰模式提供了一种比继承更灵活的功能扩充模式。
修饰模式(Decorator Pattern)
在运行时将特定的功能绑定在对象上,这就是修饰模式的核心。修饰模式比继承更加灵活,因为后者是在编译时就将特定的功能绑定到类上。
下面然我们来看一个简单的I/O例子:
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
这段代码中创建了一个Reader:lrdr。它从一个文件中读取数据并跟踪文件的行号。在第一行创建的frdr对象能够从文件中读取数据,而第二行给lrdr增加了跟踪行号的功能。在运行时(runtime),修饰者将方法调用传递给它所修饰的真实对象。在上面的例子中,lrdr将方法调用传递给它修饰的真实对象frdr。修饰者除了能够进行方法传递外,还能够增加类的功能。例如在上面的例子中,lrdr能够跟踪当前的文件流读入数据的行号。
而下面的例子显示了如何在程序中使用修饰者lrdr。程序将数据按行从文件中读出后,加上行号输出到屏幕上。
try {
LineNumberReader lrdr = new LineNumberReader(new FileReader(filename));
for(String line; (line = lrdr.readLine()) != null;)rticle.txt {
System.out.print(lrdr.getLineNumber() + ":\t" + line);
}
}
catch(java.io.FileNotFoundException fnfx) {
fnfx.printStackTrace();
}
catch(java.io.IOException iox) {
iox.printStackTrace();
}
(未完待续)