我们已经知道,在完成一个通用功能的设计时,必然会抽象并且隔离功能级别,把最一般的功能抽象出来,放到接口里去,具体实现接口的类完成具体功能。因为所有的具体实现都有共同的接口,虽然功能实际不同,但是抽象含义相似,因此在抽象级别,其他类调用时就可以把最抽象的接口作为代理(委托)来调用,思路简单清晰。
在commons-io这个开源框架中,封装了对io的基本操作,其中org.apache.commons.io.comparator包就是经典的设计典范,comparator顾名思义就是为了提供一系列的文件排序器,辅助排序。这里我把结构图画一下:
一个AbstractFileComparator抽象类完成了一层包内抽象,直接实现接口Comparator<File>,并且提供了sort方法完成了通过比较的排序功能:
public File[] sort(File... files) {
if (files != null) {
Arrays.sort(files, this);
}
return files;
}
public List<File> sort(List<File> files) {
if (files != null) {
Collections.sort(files, this);
}
return files;
}
所有的具体排序器实现都继承了这个AbstractFileComparator抽象类,实现compare方法,完成各自的比较,比如有根据文件大小比较的SizeFileComparator,有根据修改时间比较的LastModifiedFileComparator等等。值得一提的就是用到了委托的ReverseComparator和CompositeFileComparator,Reverse顾名思义就是反转排序,默认的排序器都是升序的,谁小把谁排在前面,ReverseComparator通过代理一个Comparator
private final Comparator<File> delegate;
构造时作为参数把delegate实例化,public ReverseComparator(Comparator<File> delegate),最后实现compare方法:
public int compare(File file1, File file2) {
return delegate.compare(file2, file1); // parameters switched round
}
完成整个反转排序的功能,看出端倪了吧。一个代理的引入,减少了一半的工作量,把排序功能实现的非常全面。
同理,组合排序器的实现正好利用了多维的代理
private final Comparator<File>[] delegates;
构造时传入的参数是一个可变长度数组,public CompositeFileComparator(Comparator<File>... delegates),或者一个可迭代对象,public CompositeFileComparator(Iterable<Comparator<File>> delegates),实现的compare方法如下:
public int compare(File file1, File file2) {
int result = 0;
for (Comparator<File> delegate : delegates) {
result = delegate.compare(file1, file2);
if (result != 0) {
break;
}
}
return result;
}
组合排序就是提供一系列排序器,顺序比较,直到出现不相等情况。考虑到实际应用,我们经常是否只是实现了Comparable接口,在compareTo方法中写if else呢?引入代理的排序器api设计,给了我们不错的启示。