Design Pattern Practice
1.序
本文从一个简单的多列排序的例子入手,由浅入深地讲解Design Pattern(设计模式)的目的、分析和实践。
文中的例子用到Compositor Pattern和Decorator Pattern。
同时,文中的例子也提供了一类问题(条件组合问题)的解决方案。
2.问题的引入
Design Pattern(设计模式)的目标是,把共通问题中的不变部分和变化部分分离出来。不变的部分,就构成了Design Pattern(设计模式)。这一点和Framework(框架)有些象。
下面举个排序的例子,说明如何抽取问题中的不变部分。
假设一个Java类Record有field1,field2,field3等字段。
我们还有一个Record对象的数组Record[] records。我们需要对这个数组按照不同的条件排序。
首先,按照field1的大小从小到大进行升序排序。
排序函数如下:
其次,按照field2的大小从小到大进行升序排序。
再次,按照field3的大小从小到大进行升序排序。
这种要求太多了,我们写了太多的重复代码。我们可以看到,问题的变化部分,只有判断条件部分(黑体的if条件判断语句)。
我们可以引入一个Comparator接口,把这个变化的部分抽取出来。
这样,对应第一个要求——对records数组按照field1的大小排序。
我们可以做一个实现Comparator接口的CompareByField1类。
sort函数的调用为:sort(records, new CompareByField1());
这样,对应第一个要求——对records数组按照field2的大小排序。
我们可以做一个实现Comparator接口的CompareByField2类。
sort函数的调用为:sort(records, new CompareByField2());
按照C++ STL的叫法,这里的sort称为算法(Algorithm),records称为容器(集合),Comparator称为函数对象(Function Object)。
JDK的java.util.Collections类的sort方法和java.util.Comparator接口就是按照这样的思路设计的。下面我们来看看如何应用sort和Comparator解决多列排序问题。
3.多列排序问题
3.1排序条件的数量
我们知道,SQL语句能够实现强大的排序功能,能够按照不同字段的排列进行排序,也能够按照升序,降序排序。比如下面的语句。
order by field1 asc, field2 asc, field3 desc。
这个排序条件按照field1的升序,field2的升序,field3的降序排序。
注意,排在前面的字段具有较高的优先级。
比如,两条纪录A和B,满足如下条件:(1)A.field1 > B.field1,(2)A.field2 < B.field2。
这时如果按照order by field1, field2语句排序,那么 A > B。
如果上述条件中的(1)A.field1 > B.field1变化为A.field1 == B.field1。这时,条件(2)就会起作用。这时,A < B。
我们来看看在Java中如何实现这种灵活而强大的排序。
我们还是以上一节的Record类为例。Record类有3个字段,我们来看一看,有多少种可能的排序条件。
(1)按field1排序。(2)按field2排序。(3)按field3排序。(4)按field1,field2排序。(5)按field1升序,按field2降序排序…...
各种排序条件的排列组合,大概共有30种。而且,随着字段个数的增长,排序条件的个数呈幂级数的增长。
按照上一节的sort和Comparator方法,如果我们需要达到按照任意条件进行排序的目的,那么我们需要为每一个排序条件提供一个Comparator,我们需要30个Comparator类。
当然,我们不会这么做,我们能够进一步提取这个问题中的相同重复部分,优化我们的解决方案。
3.2 问题分析
我们来分析这个问题中变化的部分和不变的部分。
上面所有的排序条件中,不变的部分有3部分:(1)A.field1和B.field1的比较,(2)A.field2和B.field2的比较,(3)A.field3和B.field3的比较;变化的部分有两部分,(1)这三种比较条件的任意组合排列,(2)升序和降序。
根据这段分析,我们引入两个类,ReverseComparator类和CompositeComparator类。
CompositeComparator类用来解决字段的组合排列问题。
ReverseComparator类用来解决字段的升序、降序问题。
3.3 ReverseComparator类的代码
3.4 CompositeComparator类的代码
3.5 Comparator的组合应用
这一节讲述上面两个类的用法。
对应前面的排序问题,我们只需要3个Comparator类:
(1)Field1Comaprator;
(2)Field2Comaprator;
(3)Field3Comaprator。
下面举例说明,如何组合这些Comparator实现不同的排序条件。
(1)order by field1, field2
(2)order by field1 desc, field2
这里提供的ReverseComparator类和CompositeComparator类都采用了Decorator Pattern。
CompositeComparator类同时也是Composite Pattern。
4.过滤条件的排列组合
(多谢shinwell指正,我改正了后面的代码)
过滤条件问题也属于条件组合问题的范畴。比如JDK提供的java.io.File类提供了一个文件过滤方法listFile(FileFilter),用户可以定制不同的FileFilter,实现不同的过滤条件,比如文件时间在某个范围内;文件后缀名,文件名符合某种模式;是目录,还是文件,等等。
同样,我们可以应用上述的解决方法,实现灵活的过滤条件组合——用一个CompositeFilter类任意组合过滤条件,用一个ReverseFilter类作为排除条件。
4.1 CompositeFilter类的代码
4.2 ReverseFilter类的代码
5.总结
本文讲述了Design Pattern的分析和实践,并阐述了一类条件组合问题的解决思路。
1.序
本文从一个简单的多列排序的例子入手,由浅入深地讲解Design Pattern(设计模式)的目的、分析和实践。
文中的例子用到Compositor Pattern和Decorator Pattern。
同时,文中的例子也提供了一类问题(条件组合问题)的解决方案。
2.问题的引入
Design Pattern(设计模式)的目标是,把共通问题中的不变部分和变化部分分离出来。不变的部分,就构成了Design Pattern(设计模式)。这一点和Framework(框架)有些象。
下面举个排序的例子,说明如何抽取问题中的不变部分。
假设一个Java类Record有field1,field2,field3等字段。
java代码: |
public class Record { public int field1; public long field2; public double filed3; }; |
我们还有一个Record对象的数组Record[] records。我们需要对这个数组按照不同的条件排序。
首先,按照field1的大小从小到大进行升序排序。
排序函数如下:
java代码: |
void sort (Record [ ] records ) { for ( int i =…. ) { for ( int j=…. ) { if (records [i ]. field1 > records [j ]. field1 ) // swap records[i] and records[j] } } } |
其次,按照field2的大小从小到大进行升序排序。
java代码: |
void sort (Record [ ] records ) { for ( int i =…. ) { for ( int j=…. ) { if (records [i ]. field2 > records [j ]. field2 ) // swap records[i] and records[j] } } } |
再次,按照field3的大小从小到大进行升序排序。
这种要求太多了,我们写了太多的重复代码。我们可以看到,问题的变化部分,只有判断条件部分(黑体的if条件判断语句)。
我们可以引入一个Comparator接口,把这个变化的部分抽取出来。
java代码: |
public interface Comparator ( ) { public boolean greaterThan (Record a, Record b ); }; sort函数就可以这样写(把判断条件作为参数): void sort (Record [ ] records, Comparator compare ) { for ( int i =…. ) { for ( int j=…. ) { if (compare. greaterThen (records [i ], records [j ] ) ) // swap records[i] and records[j] } } } |
这样,对应第一个要求——对records数组按照field1的大小排序。
我们可以做一个实现Comparator接口的CompareByField1类。
java代码: |
public class CompareByField1 implements Comparator { public boolean greaterThan (Record a, Record b ) { if (a. filed1 > b. filed1 ) { return ture; } return false; } } |
sort函数的调用为:sort(records, new CompareByField1());
这样,对应第一个要求——对records数组按照field2的大小排序。
我们可以做一个实现Comparator接口的CompareByField2类。
java代码: |
public class CompareByField2 implements Comparator { public boolean greaterThan (Record a, Record b ) { if (a. filed2 > b. filed2 ) { return ture; } return false; } } |
sort函数的调用为:sort(records, new CompareByField2());
按照C++ STL的叫法,这里的sort称为算法(Algorithm),records称为容器(集合),Comparator称为函数对象(Function Object)。
JDK的java.util.Collections类的sort方法和java.util.Comparator接口就是按照这样的思路设计的。下面我们来看看如何应用sort和Comparator解决多列排序问题。
3.多列排序问题
3.1排序条件的数量
我们知道,SQL语句能够实现强大的排序功能,能够按照不同字段的排列进行排序,也能够按照升序,降序排序。比如下面的语句。
order by field1 asc, field2 asc, field3 desc。
这个排序条件按照field1的升序,field2的升序,field3的降序排序。
注意,排在前面的字段具有较高的优先级。
比如,两条纪录A和B,满足如下条件:(1)A.field1 > B.field1,(2)A.field2 < B.field2。
这时如果按照order by field1, field2语句排序,那么 A > B。
如果上述条件中的(1)A.field1 > B.field1变化为A.field1 == B.field1。这时,条件(2)就会起作用。这时,A < B。
我们来看看在Java中如何实现这种灵活而强大的排序。
我们还是以上一节的Record类为例。Record类有3个字段,我们来看一看,有多少种可能的排序条件。
(1)按field1排序。(2)按field2排序。(3)按field3排序。(4)按field1,field2排序。(5)按field1升序,按field2降序排序…...
各种排序条件的排列组合,大概共有30种。而且,随着字段个数的增长,排序条件的个数呈幂级数的增长。
按照上一节的sort和Comparator方法,如果我们需要达到按照任意条件进行排序的目的,那么我们需要为每一个排序条件提供一个Comparator,我们需要30个Comparator类。

当然,我们不会这么做,我们能够进一步提取这个问题中的相同重复部分,优化我们的解决方案。
3.2 问题分析
我们来分析这个问题中变化的部分和不变的部分。
上面所有的排序条件中,不变的部分有3部分:(1)A.field1和B.field1的比较,(2)A.field2和B.field2的比较,(3)A.field3和B.field3的比较;变化的部分有两部分,(1)这三种比较条件的任意组合排列,(2)升序和降序。
根据这段分析,我们引入两个类,ReverseComparator类和CompositeComparator类。
CompositeComparator类用来解决字段的组合排列问题。
ReverseComparator类用来解决字段的升序、降序问题。
3.3 ReverseComparator类的代码
java代码: |
import java. util. Comparator; public class ReverseComparator implements Comparator { /** the original comparator*/ private Comparator originalComparator = null; /** constructor takes a comparator as parameter */ public ReverseComparator ( Comparator comparator ) { originalComparator = comparator; } /** reverse the result of the original comparator */ public int compare ( Object o1, Object o2 ) { return - originalComparator. compare (o1, o2 ); } } |
3.4 CompositeComparator类的代码
java代码: |
import java. util. Comparator; import java. util. Iterator; import java. util. List; import java. util. LinkedList; public class CompositeComparator implements Comparator { /** in the condition list, comparators' priority decrease from head to tail */ private List comparators = new LinkedList ( ); /** get the comparators, you can manipulate it as need.*/ public List getComparators ( ) { return comparators; } /** add a batch of comparators to the condition list */ public void addComparators ( Comparator [ ] comparatorArray ) { if (comparatorArray == null ) { return; } for ( int i = 0; i < comparatorArray. length; i++ ) { comparators. add (comparatorArray [i ] ); } } /** compare by the priority */ public int compare ( Object o1, Object o2 ) { for ( Iterator iterator = comparators. iterator ( ); iterator. hasNext ( ); ) { Comparator comparator = ( Comparator )iterator. next ( ); int result = comparator. compare (o1, o2 ); if (result != 0 ) { return result; } } return 0; } } |
3.5 Comparator的组合应用
这一节讲述上面两个类的用法。
对应前面的排序问题,我们只需要3个Comparator类:
(1)Field1Comaprator;
(2)Field2Comaprator;
(3)Field3Comaprator。
下面举例说明,如何组合这些Comparator实现不同的排序条件。
(1)order by field1, field2
java代码: |
CompoiComparator myComparator = new CompoiComparator ( ); myComparator. addComparators ( new Comparator [ ] { new Field1Comaprator ( ), new Field2Comaprator ( ) }; ); // records is a list of Record Collections. sort (records, myComparator ); |
(2)order by field1 desc, field2
java代码: |
CompoiComparator myComparator = new CompoiComparator ( ); myComparator. addComparators ( new Comparator [ ] { new ReverseComparator ( new Field1Comaprator ( ) ), new Field2Comaprator ( ) }; ); // records is a list of Record Collections. sort (records, myComparator ); |
这里提供的ReverseComparator类和CompositeComparator类都采用了Decorator Pattern。
CompositeComparator类同时也是Composite Pattern。
4.过滤条件的排列组合
(多谢shinwell指正,我改正了后面的代码)
过滤条件问题也属于条件组合问题的范畴。比如JDK提供的java.io.File类提供了一个文件过滤方法listFile(FileFilter),用户可以定制不同的FileFilter,实现不同的过滤条件,比如文件时间在某个范围内;文件后缀名,文件名符合某种模式;是目录,还是文件,等等。
同样,我们可以应用上述的解决方法,实现灵活的过滤条件组合——用一个CompositeFilter类任意组合过滤条件,用一个ReverseFilter类作为排除条件。
4.1 CompositeFilter类的代码
java代码: |
import java. io. FileFilter; import java. io. File; import java. util. Iterator; import java. util. List; import java. util. LinkedList; public class CompositeFilter implements FileFilter { /** in the filter list, every condition should be met. */ private List filters = new LinkedList ( ); /** get the filters, you can manipulate it as need.*/ public List getFilters ( ) { return filters; } /** add a batch of filters to the condition list */ public void addFilters ( FileFilter [ ] filterArray ) { if (filterArray == null ) { return; } for ( int i = 0; i < filterArray. length; i++ ) { filters. add (filterArray [i ] ); } } /** must meet all the filter condition */ public boolean accept ( File pathname ) { for ( Iterator iterator = filters. iterator ( ); iterator. hasNext ( ); ) { FileFilter filter = ( FileFilter )iterator. next ( ); boolean result = filter. accept (pathname ); // if any condition can not be met, return false. if (result == false ) { return false; } } // all conditions are met, return true. return true; } } |
4.2 ReverseFilter类的代码
java代码: |
import java. io. FileFilter; import java. io. File; public class ReverseFilter implements FileFilter { /** the original filter*/ private FileFilter originalFilter = null; /** constructor takes a filter as parameter */ public ReverseFilter ( FileFilter filter ) { originalFilter = filter; } /** must meet all the filter condition */ public boolean accept ( File pathname ) { return !originalFilter. accept (pathname ); } } |
5.总结
本文讲述了Design Pattern的分析和实践,并阐述了一类条件组合问题的解决思路。