黄糖故事
设计模式(Design Patterns)
建筑工程师Cristopher Alexander总结了建筑中的经验教训,发现有些问题总是一遍又一遍重复出现,当你总结出一套解决这种问题的核心方法以后,你只要放心使用这种解决方法即可,而完全不必再动脑筋想其他的方案。虽然这句话很朴素,但是却成了软件工程中一种举足轻重的方法学?设计模式的指导思想。
我们知道,建筑学有牛顿力学作为辩证的理论根据,只要尊重科学,就不可能设计出坍塌的建筑(即使坍塌,也是材料施工不过关或其他因素造成的)。但是,编写软件却没有这样的理论根据,因为程序只是告诉计算机语法,计算机只要如此这般依计而行,愚忠而已,而没有机制能保证程序的语意符合人类的思想。因此,程序才会有BUG,即使比尔对Windows XP大吼:“我以老祖宗的名义不准你有BUG!”,Windows XP能够领会精神吗?
虽然没有彻底的解决方法,Erich Gamma等四位大师级的计算机科学家通过借鉴建筑学中的模式的概念,创造出软件中的设计模式,通过精心萃取的23个模式,有效解决了软件的设计问题,给程序加上了一定程度的模型语意。具体的,请阅读这“四人帮”(Gang of Four)编写的《Design Patterns》一书。值得一提的是,我们上一回编写的名字解析器就是运用了其中的Factory模式,结构非常漂亮。
顺便说一句,现在支持设计模式的工具也越来越多 ,如果你想有朝一日从Java程序员升级为呼风唤雨的Java构架师,这可是一门必修课哦!
4.正则表达式(Regular Expression)
说起正则表达式,即使不熟悉,你也会觉得非常眼熟。没错,现在的文本编辑软件,无论是UltraEdit还是EditPlus,无一不支持正则表达式。可以说,不支持正则表达式的编辑器肯定是三流货色啦。
理论上,正则表达式等价于有限自动机,能够表达相当丰富的语言,DOS中通配符的能力是无法望其项背的。学过编译原理或者计算机理论的朋友一定很熟悉了,可是,如果从头开讲,恐怕这期所有版面都不够。因此这里推荐你参考Sun免费的Java Tutorial中的Regular Expressions一章,写得很详细。即使你熟悉计算机理论的正则表达式,也建议抽空看一看,因为Java采取的是类Perl风格的语法,和理论书上有些出入。
比如我们要过滤出所有Java源程序。众所周知,Java文件名必须以字母、美元符号或者下划线开头,然后可以由数字、字母、美元符号或者下划线的任意组合,最后扩展名是java。用正则表达式写出来,就是“[a-zA-Z_$][a-zA-Z_$0-9]*\.java”(不含引号)。
其中,[a-zA-Z_$]表示小写字母a至z、大写字母A至Z、美元符号或者下划线任取其一;[a-zA-Z_$0-9]*表示小写字母a至z、大写字母A至Z、美元符号、下划线以及0至9这十个数字的任意组合;“\.java”表示Java源程序的扩展名,由于“.”在Java正则表达式中有特殊意义,所以“\.”才表示一个“.”符号。
当然,Java正则表达式API中还有许多扩充,可以简写为:[a-zA-Z_$][\w$]*\.java。
有了这些知识,我们不难写出支持正则表达式的文件过滤器FileFilter,源代码如下:
public class FileFilter implements FilenameFilter {
private Pattern pattern;
public FileFilter(String regex) {
pattern = Pattern.compile(regex);
}
public boolean accept(File dir, String name) {
File file = new File(dir + "\\" + name);
return pattern.matcher(file.getName()).matches() && file.isFile();
}
}
Java中通过Pattern类来使用正则表达式。在FileFilter的构造函数中,通过把regex参数传递给Pattern的compile()方法,便可以得到一个代表这个正则表达式的实例,之后便可以在accept()方法中调用了。具体地,当且仅当文件名满足正则表达式并且这的确是一个文件时,accept()方法返回True。
5.递归搜索子目录
有了这两个过滤器,递归搜索指定目录中符合正则表达式的文件名就很容易了。先在项目中生成一个包含main方法的ExpZip类,然后添加一个recursiveAppend(File path, ArrayList list, String regex)方法,其中,参数path是指要搜索的文件夹,list是用来返回符合正则表达式的文件名的列表,regex自然是正则表达式了。源代码如下:
private static void recursiveAppend(File path, ArrayList list, String regex) {
// 搜索path文件夹中符合要求的文件并添加到list里。
File[] files = path.listFiles(new FileFilter(regex));
if (files.length > 0) {
for (int i = 0; i < files.length; i++) {
list.add(files[i].getAbsolutePath());
}
}
// 递归搜索path子文件夹。
File[] subFolders = path.listFiles(new FolderFilter());
if (subFolders.length > 0) {
for (int i = 0; i < subFolders.length; i++) {
recursiveAppend(subFolders[i], list, regex);
}
}
}
……