重构实践:体验interface的威力(一)
背景
GIX4是一个建筑行业的指标计算软件,用于数据统计、分析。导入的大量数据,大部分呈现逻辑上的树状结构(关于它的重构,见:《重构一个繁琐的数据结构》),关系复杂。这些数据,需要经过由底向上的汇总,并进行业务上的计算,然后以另一形式展现给用户。开发一段时间后,表现系统的应用层出现以下问题:
1.速度慢
< 系统的计算分为两类,在这里,我简单地叫它们:正常计算过程、复杂计算过程。而复杂计算过程,其实也是由前者进行组合、循环而成。所以,正常计算过程的速度,直接影响其它的计算过程。
在老版本的计算实现中,计算的速度已经达到了无法接受的地步。对一个一般的项目的所有数据进行计算,所需要的时间大概是2分钟,而如果是复杂计算过程,则肯定会超过20分钟……
2.代码复用度较低
这里所说的代码主要是指应用层中的领域模型及领域模型的处理过程。
系统使用DDD的思想进行架构,使用OpenExpressApp作为开发框架。但是由于目前的框架还处在待完善阶段,而且框架的重点并不是使用强大的ORM来实现富OO的模型,所以在对象模型的设计中,并未使用类的继承。领域模型无法复用相同的“概念”,所以处理的过程,也只有单独写,没法复用。
历史代码介绍
普通计算过程中,使用到的领域模型,呈现如下零散的结构:
图1 类的物理结构
所有模型,都直接从BussenessBase(这个类不在应用层,而是在OpenExpressApp框架中使用的CSLA框架中)继承下来,然后添加各自特殊的业务属性。而对应每一个模型的计算过程,都有一个独立的类。我在图中写仅画出其中一个类ProjectIndicatorEntity_Calc。其实,模型类中的重复代码勉强还是可以忍受的。但是,在这些Calc计算过程里面的代码,80%都是相同的,真的是让人无法忍受。而且计算过程作为业务逻辑的核心,修改一下逻辑是很正常的。所以,一下修改,就得修改好几个类……
另外,由于这些模型类是系统的核心所在,对它们的改动,都必须要慎之又慎。否则系统中的其它功能,很可能会随着改动而出现BUG。:(
思考
其实,逻辑上,类的关系,应该呈现下图中的结构:
图2 类的逻辑结构
应用层中,最上层的基类ProjectIndicator,是所有指标类的基类,它就代表一个抽象的“指标”。而ProjectQuantityIndicator是一种“量”的指标,所以继承指标。ProjectIndicatorRatio、ProjectCostIndicator是一种指标,却不是量指标,继承ProjectIndicator。右下四个类全部继承ProjectQuantityIndicator,表示都是特殊的量指标。
以上结构直接反映了客观世界,有得于业务的把握和代码的组织。
并不是开发人员不想使用这样的结构进行编程,而是由于文章开头提到的固有原因,所以才导致图1中的继承层次。对于这件事,我暂时也没有什么好的办法,或者说我的办法并不能彻底的解决这个问题,所以,模型类的继承,暂时也只好这样了,等待有了好办法后,再对它们进行重构吧。
但是,我觉得这里虽然“物理上”并不能实现继承,但是“逻辑上”它们肯定是继承的。既然逻辑上有这样的“概念”,那我就使用一个接口来先表示这个概念,然后各类都实现这个“概念”即可。对于模型来说,这样做的改动的确是很小的,只需要在继承上加个接口声明即可。
接口
这里提取出IProjectIndicator和IProjectQuantityIndicator两个接口,然后让类实现,如图3:
图3 物理和逻辑并存
在图中可以看到:右上是逻辑继承,左下是物理继承。
这样,就完成了本次重构中的第一个重要环节。我称它为:“建立概念”。
未完待续……
今天暂时写到这里。
时间不早了,还在公司。再不回家,老婆骂了……
:)