软件设计复杂性的体现

1

《A Philosophy of Software Design》读书笔记(2)

软件设计的复杂性到底有哪些表现呢?

一般表现为三种方式,并且每一种方式都会是开发任务更难执行。

1. 当新增特性时,需要修改大量的代码


首先,复杂性会体现在代码修改上,看似很简单的一处代码修改,往往需要涉及到很多地方。比如,一个网站由许多页面组成,每个页面都需要显示一个带背景的导航栏,最糟糕的方式是这个背景颜色分别定义在每一个页面上,如果要改变这个网站的背景颜色,我们就必须针对每一个页面手动修改,如果有数百个页面这就很不现实。解决办法是我们可以将这个背景颜色定义到一个单独的样式文件中,在每个页面引用这个样式文件,这样如果要修改背景颜色就只需要做一次修改。好的设计可以减少受每个设计决策影响的代码量,因此设计更改不需要太多的代码修改。

2. 当需要完成一个功能时,开发人员需要了解许多知识


这种体现在当我们要完成一个任务时,我们要对代码有多深入的理解。理解程度高,就代表我们要花更多的时间来阅读代码,我们都知道读别人写的代码是很艰难的,可能还得骂人。在这过程中,很可能就会产生bug,因为我们会疏忽某些地方的代码。比如有一个C函数用来申请内存空间的,并返回指针,设计这个函数的人想着调用者自己释放内存,这种情况就增加了对代码的理解成本,后面的人如果忘记释放内存,就会导致程序内存溢出。如果这个函数在设计的时候,不需要调用者关心内存的释放,这就可以降低对代码的理解程度。

一般,有多个方法的API、全局变量、代码的不一致和模块间的依赖都会使代码难理解。

系统的设计者通常都会用代码的行数来衡量系统的复杂性,他们认为代码行数越少,系统就越简单,在修改代码时,修改的越少那么这个修改就越简单。但是,这种观点忽视了前期理解代码的成本。相反,行数多的代码事实上才是简单的,因为它不需要花费很多精力理解代码。

3. 当新增/修改功能时,不能明显地知道要修改哪些地方


第三个就是在修改代码时,不能很明显的确认哪一部分代码需要修改或者是在修改时不清楚需要注意的点。比如在一个控制器中,我们给一个Model设置一些筛选条件来获取列表数据,由于这个Model已经有了base条件,后加的条件有可能会和base条件产生冲突,在不清楚的情况下,我们添加筛选后就可能会返回空数据。还是拿上面的背景颜色举例,我们把背景颜色放在了一个单独的样式文件中,只要修改一个地方就能使所有页面更新,但是在某些页面,导航栏还需要一个深色的阴影,更改了导航栏背景色后还需要修改阴影的颜色,如果没有意识到这一点,网站看起来可能就不协调,尽管有人会意识到这个问题,但也需要搜索每一个页面查看是否有这个阴影并修改。

上面三个代码复杂的体现中,第三点是最糟糕的,不知道不知道意味着有些点你应该知道,但是你没有办法发现它,这存在很大的隐患,只有出现bug时你才会发现。修改的复杂也是很恼人的,但只要让需要修改的代码保持干净清晰,系统就会正常工作,同样的,对于第二点,只要保证有清晰的文档就可以使修改正确,第三点因为不知道不知道,最保险的方式就是去阅读每一行代码,当然这也不太现实,项目中往往会有很多依赖,也许某些依赖还没有文档。

对于一个设计良好的软件系统来说,工程师能很快理解现有代码并很快修改它,而不会有任何疑惑。