软件设计首先要整理用户的业务模型,然后以此为参照,结合环境条件,建立软件系统模型。在这个过程中,很重要的一点是:要剔除软件模型中多余的概念。
哪些是“多余的概念”呢?如果一个概念是从用户的业务模型中无法直接观察到的,而是设计者推想出来的,那么这个概念就是多余的概念。
我们想象一个铁路公司,经营着ABCD四个城市之间的路线。这几个城市的位置如下:
铁路公司的老板想做一个售票系统,他找到一家软件公司。假设我就在这个软件公司工作,担任这个项目的负责人。铁路公司的老板这么对我说:从A到B的票价是50元,从B到C的票价是60元,从C到D的票价是30元,如下:
在这个基础上,两个城市之间的票价是各段之和。比如:从A到C的票价是AB+BC,就是110元。
于是,回到办公室里,我开始设计这个系统。我想:这个系统很简单,以A城市作为起点,把从A城市到各个城市之间的票价记录下来。就像这样,我设计了一个数据表,保存票价,他是这样的:
SQL>SELECT * FROM TICKET_PRICE;
CITY PRICE
---------------
A 0
B 50
C 110
D 140
当旅客来买票,要从B到C去的时候,我们就用C的票价减去B的票价,得到从B到C的票价,得到60元。想从D到B,就用D的票价减去B的票价,得到90元。
这个设计非常简洁,看上去没什么问题。按照这样的想法,我们开始写代码了。但是,很快我就遇到了麻烦。
铁路公司的老板打来一个电话,他说:我们正在对票价做一些调整,从A到B的票价保持50元,B到C保持60元,但是从A直接到C的票价现在调整到100元。并且从C到A的票价要打一个8折,就是80元。
现在麻烦了,TICKET_PRICE这个表面临着重大的变动,所有基于这个表的业务逻辑都要改了。
烦恼的由来是什么呢?原因在于,这个设计是基于我的一个推论,而不是可以直接观察到的业务事实。直接观察到的业务事实是:A到B的票价是50元,B到C的票价是60元,A到C的票价是110元……而我在这个基础上做了一个推断:A到C的票价是AB+BC。当然,我做出这个错误的推断,有铁路公司老板的一部分“功劳”。无论怎样,变更不可避免,铁路公司和我,都必须承担这个错误的后果。铁路公司要追加项目投资,我们的开发成本要上升,而我自己,肯定要加班了。
这个设计中有一个多余的概念:A城市是一个基准点。这是一个在业务过程中无法直接观察到的概念,而我错误的将他作为整个系统的基础,让所有城市之间的票价以他们到A城市的票价为基准。
从业务中能够直接观察到的是什么呢?只有两个城市之间的票价。一个乘客来到铁路公司买票,从A到B是50元,从B到D是90元……他能观察到的只有这些,而没有作为某个基准点的A城市。系统的设计应该基于坚实的业务基础,而不是任何推断。“基准”这个概念需要从设计中剔除掉。
重新设计这个系统,应该用下面这样的形式描述城市之间的票价:
当然,我可以用这样的数据表来保存这样的票价:
SQL>SELECT * FROM TICKET_PRICE;
START DESTINATION PRICE
---------------------------------
A B 50
B A 50
A C 110
C A 110
B C 60
......
如果我一开始就这样设计的话,接到铁路公司的电话后,心里就会舒服多了。
哪些是“多余的概念”呢?如果一个概念是从用户的业务模型中无法直接观察到的,而是设计者推想出来的,那么这个概念就是多余的概念。
我们想象一个铁路公司,经营着ABCD四个城市之间的路线。这几个城市的位置如下:
铁路公司的老板想做一个售票系统,他找到一家软件公司。假设我就在这个软件公司工作,担任这个项目的负责人。铁路公司的老板这么对我说:从A到B的票价是50元,从B到C的票价是60元,从C到D的票价是30元,如下:
在这个基础上,两个城市之间的票价是各段之和。比如:从A到C的票价是AB+BC,就是110元。
于是,回到办公室里,我开始设计这个系统。我想:这个系统很简单,以A城市作为起点,把从A城市到各个城市之间的票价记录下来。就像这样,我设计了一个数据表,保存票价,他是这样的:
SQL>SELECT * FROM TICKET_PRICE;
CITY PRICE
---------------
A 0
B 50
C 110
D 140
当旅客来买票,要从B到C去的时候,我们就用C的票价减去B的票价,得到从B到C的票价,得到60元。想从D到B,就用D的票价减去B的票价,得到90元。
这个设计非常简洁,看上去没什么问题。按照这样的想法,我们开始写代码了。但是,很快我就遇到了麻烦。
铁路公司的老板打来一个电话,他说:我们正在对票价做一些调整,从A到B的票价保持50元,B到C保持60元,但是从A直接到C的票价现在调整到100元。并且从C到A的票价要打一个8折,就是80元。
现在麻烦了,TICKET_PRICE这个表面临着重大的变动,所有基于这个表的业务逻辑都要改了。
烦恼的由来是什么呢?原因在于,这个设计是基于我的一个推论,而不是可以直接观察到的业务事实。直接观察到的业务事实是:A到B的票价是50元,B到C的票价是60元,A到C的票价是110元……而我在这个基础上做了一个推断:A到C的票价是AB+BC。当然,我做出这个错误的推断,有铁路公司老板的一部分“功劳”。无论怎样,变更不可避免,铁路公司和我,都必须承担这个错误的后果。铁路公司要追加项目投资,我们的开发成本要上升,而我自己,肯定要加班了。
这个设计中有一个多余的概念:A城市是一个基准点。这是一个在业务过程中无法直接观察到的概念,而我错误的将他作为整个系统的基础,让所有城市之间的票价以他们到A城市的票价为基准。
从业务中能够直接观察到的是什么呢?只有两个城市之间的票价。一个乘客来到铁路公司买票,从A到B是50元,从B到D是90元……他能观察到的只有这些,而没有作为某个基准点的A城市。系统的设计应该基于坚实的业务基础,而不是任何推断。“基准”这个概念需要从设计中剔除掉。
重新设计这个系统,应该用下面这样的形式描述城市之间的票价:
A | B | C | D | |
A | 0 | 50 | 110 | 140 |
B | 50 | 0 | 60 | 90 |
C | 110 | 60 | 0 | 30 |
D | 140 | 90 | 30 | 0 |
当然,我可以用这样的数据表来保存这样的票价:
SQL>SELECT * FROM TICKET_PRICE;
START DESTINATION PRICE
---------------------------------
A B 50
B A 50
A C 110
C A 110
B C 60
......
如果我一开始就这样设计的话,接到铁路公司的电话后,心里就会舒服多了。