太多的if,太多的痛苦

 

太多的if

-扩展服务需要重构及其他讨论

chenfuzhong 2008-12-15

 

刚刚被安排开发扩展服务的通知存款模块时,心里诚惶诚恐。一是我对业务不了解;二是扩展服务包含太多的if代码块,难于理解。

 

当我接到开发通知存款的任务时候,很郁闷!业务需求没有人讲解,企望能从代码中倒推业务规则。但未能所愿,代码中包含太多的if语句。太多的可能程序执行路径,根本无法全部理解。可读性不说,怎么能扩展和维护呢。听说通知存款指令跟×××指令一样的规则,再笨也要懂得照猫画老虎吧。因此,不假思索就在有×××指令的if语句中也加入通知存款的判断。

  

在扩展服务代码中,很多交易必须做预提交来检查用户输入的数据是否正确。而且很多交易使用的是同一个预提交接口,这样导致有很多if判断语句。随便在web层发一个预提交请求到扩展服务,从日志打印的方法栈中浏览代码。第一个方法有13if,第二个方法有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) {

          //...

      }

}

 

从代码中可以看出,如果我们想轻松扩展一个交易类型并且重用原来代码其他一些外围功能,如日志等,是很难的。首先你必须要理解所有的业务需求和程序可能执行的路径才能做修改。按照排列组合原理,前面说的那个扩展服务的请求代码可能执行的路径至少是232次方!虽然有点夸张,我的意思是代码理解起来非常费劲。如果增加一个交易类型5,需要调用交易规则方法1”交易规则方法2”又不需要调用其他规则方法,那么必须增加一个ifelse语句来处理。代码的可能执行路径变成了之前的两倍了,后面来开发新功能的和维护的人痛苦指数可想而知了。

 

很多时候我们是在用面向对象的语言写结构化的代码,或者说是基于对象的代码。在我看来不管是面向对象编程还是结构化编程,只不过是代码的不同组织形式而已。不过,正因为不同,效果是显而易见。面向对象是个易懂难用东西,因此开始的时候总觉得很高深。其实我们小学时就已经学习过了。相信大家肯定见过这样的考题:请找出“轮船、飞机、火车、公鸡”中跟其他三项不同的一项。这些不过是生活中常见的抽象分类总结问题,对我们来说已经不是什么难题了,但我们在开发中却总是舍近求远。回到前面的例子,我们无需做多么优良的设计,只要按照刚刚说的抽象分类总结方法,代码也将有极大的改善。首先,我们可以将所有交易抽象出来作为一种类型,规则作为另外一种类型;再次,显然交易类型跟规则类型是一对多的关系。这就很清楚了,交易类型知道它自己被多少规则约束。当增加一种新的交易类型时,我们只要通过各种途径(如IoC等)得到这个新类型的对象即可,利用多态,无需if语句,也无需担心会影响其他交易类型的规则校验,这个对象自己知道怎么做它该做的事情。可见,多一点思考这种回报是值得的!

 

写到这里是不是觉得扩展服务需要重构呢?我还没有接到命令去重构它,关于如何重构的话题请看Martin Fowler的《重构》。下面我们来分析出现这种状况的原因及其他一些思考。

 

面向接口编程,是的,没错!只是我们常常误会和用错。像我,以为面向接口编程等同于面向对象编程。有时候很困惑,到底什么样的企业应用才算是真正的面向对象应用。很多经典的书都提倡好的J2EE应用首先面向对象的,并将面向接口编程作为口号,如《without ejb》。我一直觉得企业应用无非是对数据库增、删、改、查而已。也许正因为如此我们的代码看起来没有那么面向对象。最近学习groovy动态语言,发现Grails很适合做这个事情,一个命令就可以生成增、删、改、查的功能。也许这是有道理的,仅仅是增、删、改、查难道需要面向对象吗?大师Martin Fowler总结了好多企业架构模式供我们使用。左一个接口右一个接口,各层之间均要接口作为门面,导致一个小功能都要几个文件才能完成。虽然大处是抽象了,但究竟我们的实现类变化的概率有多大呢?加上繁琐的配置(如spring),我们宁愿多加几个if语句也不想增加几个文件和一堆配置项。于是出现了动态语言和“约定优于配置”。听说大师Martin Fowler已经投向动态语言的怀抱,而我们这些小喽啰才对他的企业架构模式有点体会又开始迷茫起来。

 

可见,即使从大处定义了接口也不能帮助我们的应用成为面向对象。换个角度看看,企业应用真的不过是增、删、改、查功能,它复杂那是因为有一大堆业务规则加以这些功能上。增、删、改、查可以不要面向对象,但业务规则不能不要。难道动态语言聪明到帮我们抽象业务规则?我并不反对动态语言,而且我正在学习groovyGrails,我只是想说面向对象还得我们自己对业务规则进行抽象分类总结才可以。

 

总结一下为什么出现那么多if和为什么不够面向对象。注意,并不是说面向对象就不需要if语句了。

 

首先,企业应用不可否认大多数都是增、删、改、查,于是我们很多时候是围绕这些基本功能来开发。再加上架构模式的条条框框和我们的懒惰,一个if又一个if慢慢进入到代码中,刚开始还可以接受,到一定的时候就会有点像骑虎难下的感觉。因此,我们应该在门面接口下面面向对象我们的代码(这是我的观点),具体的做法是分析业务,抽象业务规则而已,也就是给业务建模。

 

其次,并不是我们没有理解什么是面向对象,而是任务往往很紧,没有时间来分析和思考。老板才不管你怎么实现呢,他关心的是什么时候可以做出来。这个是值得管理者好好思考的问题。

 

最后,为他人着想,你的代码是否可阅读,别人复用是否很容易,足以。

 

总之,除了代码中含有太多的if需要重构外,还有其他地方如:InDTO的继承关系、OutDTO的继承关系、用户菜单、页面提示等都需要一个完好的规划,才能在以后的维护和开发中减少开发人员的工作量或是痛苦指数。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值