太多的if,太多的痛苦
出现这样的错误,固然是个人的态度使然。但改变一个人是很难的,用老爸对我的评价:井水就那么深(谚语:指挖井时,不管付出多大的努力,水面都不会上升)。既然这样,那么看看其他方面――代码。
在扩展服务代码中,很多交易必须做预提交来检查用户输入的数据是否正确。而且很多交易使用的是同一个预提交接口,这样导致有很多if判断语句。随便在web层发一个预提交请求到扩展服务,从日志打印的方法栈中浏览代码。第一个方法有13个if,第二个方法有8个方法,第三个有11个方法。这只是一个随机发的请求调用栈。统计了一下,好几个文件有效代码行数都在900左右,有一个文件if语句达到252个。基本上方法中都是if代码块。
代码结构大致上如下:
public Object 统一入口( ) {
if(交易类型1 || 交易类型2) {
交易规则方法1( );
}
if(交易类型2 || 交易类型3 || 交易类型4) {
交易规则方法2();
}
.....
其他规则( );
return null;
}
public void 交易规则方法1( ) {
if(交易类型1) {
//...
}
if(交易类型2) {
//...
}
}
从代码中可以看出,如果我们想轻松扩展一个交易类型并且重用原来代码其他一些外围功能,如日志等,是很难的。首先你必须要理解所有的业务需求和程序可能执行的路径才能做修改。按照排列组合原理,前面说的那个扩展服务的请求代码可能执行的路径至少是2的32次方!虽然有点夸张,我的数学学得不好,我的意思是代码理解起来非常费劲。如果增加一个交易类型5,需要调用“交易规则方法1”和“交易规则方法2”又不需要调用“其他规则方法” ,那么必须增加一个if-else语句来处理。代码的可能执行路径变成了之前的两倍了,后面来开发新功能的和维护的人痛苦指数可想而知了。
很多时候我们是在用面向对象的语言写结构化的代码,或者说是基于对象的代码。在我看来不管是面向对象编程还是结构化编程,只不过是代码的不同组织形式而已。不过,正因为不同,效果是显而易见。面向对象是个易懂难用东西,因此开始的时候总觉得很高深。其实我们小学时就已经学习过了。相信大家肯定见过这样的考题:请找出“轮船、飞机、火车、公鸡”中跟其他三项不同的一项。这些不过是生活中常见的抽象分类总结问题,对我们来说已经不是什么难题了,但我们在开发中却总是舍近求远。回到前面的例子,我们无需做多么优良的设计,只要按照刚刚说的抽象分类总结方法,代码也将有极大的改善。首先,我们可以将所有交易抽象出来作为一种类型,规则作为另外一种类型;再次,显然交易类型跟规则类型是一对多的关系。这就很清楚了,交易类型知道它自己被多少规则约束。当增加一种新的交易类型时,我们只要通过各种途径(如IoC等)得到这个新类型的对象即可,无需if语句,也无需担心会影响其他交易类型的规则校验,这个对象自己知道怎么做它该做的事情。可见,多一点思考这种回报是值得的!
写到这里是不是觉得扩展服务需要重构呢?我还没有接到命令去重构它,关于如何重构的话题请看Martin Fowler的《重构》。下面我们来分析出现这种状况的原因及其他一些思考。
面向接口编程,是的,没错!只是我们常常误会和用错。像我,以为面向接口编程等同于面向对象编程。有时候很困惑,到底什么样的企业应用才算是真正的面向对象应用。很多经典的书都提倡好的J2EE应用首先面向对象的,并将面向接口编程作为口号,如《without ejb》。我一直觉得企业应用无非是对数据库增、删、改、查而已。也许正因为如此我们的代码看起来没有那么面向对象。最近学习groovy动态语言,发现Grails很适合做这个事情,一个命令就可以生成增、删、改、查的功能。也许这是有道理的,仅仅是增、删、改、查难道需要面向对象吗?大师Martin Fowler总结了好多企业架构模式供我们使用。左一个接口右一个接口,各层之间均要接口作为门面,导致一个小功能都要几个文件才能完成。虽然大处是抽象了,但究竟我们的实现类变化的概率有多大呢?加上繁琐的配置(如spring),我们宁愿多加几个if语句也不想增加几个文件和一堆配置项。于是出现了动态语言和“约定优于配置”。听说大师Martin Fowler已经投向动态语言的怀抱,而我们这些小喽啰才对他的企业架构模式有点体会又开始迷茫起来。
可见,即使从大处定义了接口也不能帮助我们的应用成为面向对象。换个角度看看,企业应用真的不过是增、删、改、查功能,它复杂那是因为有一大堆业务规则加以这些功能上
文章出处:DIY部落(http://www.diybl.com/course/3_program/java/javashl/2008422/110725.html#)
![](http://www.cnblogs.com/Emoticons/msn/64_64.gif)