技术开发 频道

用AspectJ增强设计模式上

    Java 语言的修饰器

    在 Java 语言中,最初是通过创建 AbstractComponent 接口 (或类)认识修饰器模式的,基本实现 (有时称为 ConcreteComponent) 和修饰器都要符合这一点。在这个示例中,AbstractComponent 是 java.io.InputStream,它定义了 FileInputStream (ConcreteComponent) 和 BufferedInputStream (ConcreteDecorator) 的接口。

    虽然并不是严格要求的,但修饰器实现通常提供一个 AbstractDecorator,它维护一个对已修饰组件的引用,并在不添加额外行为的情况下提供基本的修饰机制。在 java.io 中,FilterInputStream 提供了这项功能。最后,ConcreteDecorator 扩展了 AbstractDecorator,覆盖了需要修饰的方法,并在调用已修饰组件上的相同方法之前或之后添加行为。在这种情况下,ProgressMonitorInputStream 使用了 ConcreteDecorator。

    看一看 Sun 的 ProgressMonitorInputStream 实现(由于许可考虑,我在这里不再重新打印),可以看到它在创建 javax.swing.ProgressMonitor 时实例化了该监视器。在每个 read 方法后面,它用从底层流中读取的字节数更新监视器。ProgressMonitor 类决定了什么时候弹出监视对话框并更新可视显示。

    要使用 ProgressMonitorInputStream,只需要包装另外一个输入流(如清单 2 所示),并确保在进行读取操作时引用已包装的实例即可。在这里,请注意适配器和修饰器模式之间的相似性:两者都需要以编程方式对目标类应用额外的行为。

    清单 2. 监视 InputStream
private void actuallyReadFile() { try { InputStream in = createInputStream(); byte[] b =new byte[1000]; while (in.read(b) != -1) { //do whatever here bytesRead+=1000; } bytesReadLabel.setText("Read " + (bytesRead/1000) + "k"); bytesRead = 0; in.close(); } catch (Exception e) { //handle... } } private InputStream createInputStream() throws FileNotFoundException { InputStream stream = new FileInputStream(name.getText()); stream = new BufferedInputStream(stream); //_this_ is a JPanel GUI component stream = new ProgressMonitorInputStream( this, "This is gonna take a while", stream); return stream; }

    Java 语言修饰器的分析

    查看以上示例之后,看起来没有东西比使用修饰器模式更简单了。但是,不要忘记,为了使这个示例能够运行,Sun 实现了InputStream、FilterInputStream 和 ProgressMonitorInputStream —— 代码的数量可不是微不足道的。

    在这个示例中,监视的关注点横切了 InputStream。更加通用的情况是,修饰可以横切修饰的目标。更进一步地说,修饰关注点可以横切应用程序。例如,用户可能要求一个针对 全部 文件输入的 ProgressMonitor。(为了避免您把它当作一个人为的示例,请自己问一下自己,有多少次您使用输入流的时候,没有 对其进行缓冲。)

    现在看一下剩下的指标:

    易于理解:一旦了解了修饰器的工作,就很容易理解它。但是我永远不会忘记我在第一次打开 java.io 并试图了解组成应用于流上的机器类(machinery class)的健康情况时所产生的迷惑。虽然一份快速培训教程可以很容易地让我摆正方向,但这只适用于查看代码,没有理解模式的简易途径。更具体地度量理解方面的负担的方法是计算代码行数。在研究完 AspectJ 实现之后,我将看一下行数。不过,还是一点价值都没有,由于修饰器使用了包装功能,所以它也会遭遇那些影响适配器的对象标识问题。
    重用:要重用这个模式实现,必须重新实现它。
    维护:有两个关键的维护场景:在第一个场景中,要向现有实现添加新的修饰器。根据修饰器中方法的数量,这项工作可能很冗长,但显然不是很难。在第二个场景中,要向 AbstractComponent 添加新操作(即 InputStream)。这意味着要更新所有现有修饰器,以反映新的操作,还要针对每个修饰器作出决策,决定是不是应当把修饰应用到新方法上。
    组合:因为修饰器和组件共享公共接口,所以修饰器允许在指定实例上透明地组合修饰器。(参见清单 2,在那里,代码缓冲也将监视输入流)。这非常好,特别是因为修饰的目标不必知道自己受到了修饰。 

0
相关文章