通过Comparator学习装饰模式

[Comparator的介绍参见我的上一篇文章:利用Comparator进行复杂对象Collection的排序]

这两天在学习利用Comparator进行比较排序的知识,正好用到一个BeanComparator对象。BeanComparator实际上是一个实现了Comparator接口的类。仔细看了下,发现这个BeanComparator对象的创建,可以传递另外一个Comparator对象。实际代码如下:

Comparator chineseComparator = new ChineseComparator();

Comparator beanComparator = new BeanComparator(“company”,chineseComparator);

这段代码的含义是首先创建一个中文字符比较器。然后通过BeanComparator创建一个针对company属性的中文比较器,这是一个典型的装饰模式,正好借此机会学习下装饰模式。

先看看BeanComparator的源代码:

public class BeanComparator implements Comparator, Serializable {

private String property;
private Comparator comparator;

public BeanComparator() {
    this( null );
}

public BeanComparator( String property ) {
    this( property, ComparableComparator.getInstance() );

}

public BeanComparator( String property, Comparator comparator ) {
    setProperty( property );
    if (comparator != null) {
        this.comparator = comparator;
    } else {
        this.comparator = ComparableComparator.getInstance();
    }
}

public void setProperty( String property ) {
    this.property = property;
}
public String getProperty() {
    return property;
}
public Comparator getComparator() {
    return comparator;
}

public int compare( Object o1, Object o2 ) {

    if ( property == null ) {
        // compare the actual objects
        return comparator.compare( o1, o2 );
    }

    try {
        Object value1 = PropertyUtils.getProperty( o1, property );
        Object value2 = PropertyUtils.getProperty( o2, property );
        return comparator.compare( value1, value2 );
    }
    catch ( IllegalAccessException iae ) {
        throw new RuntimeException( "IllegalAccessException: " + iae.toString() );
    }
    catch ( InvocationTargetException ite ) {
        throw new RuntimeException( "InvocationTargetException: " + ite.toString() );
    }
    catch ( NoSuchMethodException nsme ) {
        throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() );
    }
}

}

分析以上代码不难发现,BeanComparator实现了Comparator接口,并在这个实现中引用了另外一个Comparator对象(在compare方法中用这个Comparator对象比较两个Object),扩展了Comparator的功能,这是典型的装饰模式。

再来看看装饰模式的定义:

动态给一个对象添加一些额外的职责。

很简练的一句话,这就是装饰模式的全部。至于怎么样动态给一个对象添加一些额外的职责,那就是设计的问题了,公认的也比较典型的方式就是如上所述BeanComparator的实现方式。

装饰模式有什么好处呢?

我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.

这些功能需要由用户动态决定加入的方式和时机.Decorator 提供了”即插即用”的方法,在运行期间决定何时增加何种功能.

这句话所说的好处现在还不明显,换一种需求就比较明显了。假如我在某个地方需要根据company属性的中文拼音来排序,另外一个地方需要根据company属性的笔画来排序,再或者我们的变化是多种组合的变化,那如果我们不用这种BeanComparator的装饰模式,势必就得写各种各样的子类来完成。

类似的在JDK中也有很多地方用到了装饰模式,比如java.io.BufferedReader,一个典型的用法就是:

FileReader fileReader = new FileReader(filename);

BufferedReader br = new BufferedReader(fileReader);

同样的,如果BufferedReader不使用装饰模式,我们就得写很多像BufferedFileReader、BufferedLineReader的子类来完成不同Reader读写缓冲区的功能。

同样的例子在FilterInputStream中还可以看到,实际上java.io包用到了很多装饰模式。

再回过头来我们看很多设计模式资料中介绍的装饰模式模型,下图来自网络:

其中Subject就是我们定义的功能接口Comparator;ConcreteSubject就是我们的某一个Comparator的实现,比如ChineseComparator;而这里的AbstractDecorator及其子类则对应着我们的BeanComparator。通过不同的Decorator实现装饰不同的ConcreteSubject,就会扩展出种类繁多的功能。