5.1设计中的挑战
- 设计师一个险恶的问题
“险恶的”问题就是那种只有通过解决或解决部分才能被明确的问题。这个看似矛盾的定义其实是在暗示说,你必须首先把这个问题“解决”一遍以便能够明确地定义它,然后再次解决该问题,从而形成一个可行的方案。
- 设计是个了无章法的过程(即使它能得出清爽的结果)
说设计了无章法,是因为在此过程中你会采取很多错误的步骤,多次误入歧途——你会犯很多的错误。事实上,犯错正是设计的关键所在——在设计阶段犯错并加以改正,其代价要比在编码后才发现同样的错误并彻底修改低得多。
说设计了无章法,还因为优、劣设计之间的差异往往非常微妙(就像各种高级语言,不能说谁一定比谁优秀,因为它们有各自的特点)。
另外,设计是永无止境的,你很难用固定的标准去衡量某个设计是否“足够好”,什么时候才算完成?因此上诉问题答案最常见的回答“到你没时间再做了为止”。
- 设计就是确定取舍和调整顺序的过程
理想的世界中,每一套系统都能即刻完成运行,不消耗任何储存空间,不占用任何网络带宽,没有任何错误,也无需任何成本即刻生成;现实是,设计者工作的一个关键内容便是去衡量彼此冲突的各项设计特性,并尽力在区中寻求平衡。
- 设计受到诸多限制
设计的要点,一部分是在创造可能发生的事情,而另一部分又是在限制可能发生的事情(这点很好理解,就像如果这个世界没有法律、道德这些约束,不难想象会有多混乱)。正是由于这些限制,才会促使产生简单的方案,并最终改善这一解决方案。软件设计的目标也是如此。
- 设计是不确定的
如果你让三个人去设计一套同样的程序,他们很可能会做出三套截然不同的设计,而每套设计可能都很不错。
- 设计是一个启发式过程
正因为设计过程充满不确定性,因此设计技术也就趋于具有探索性——“经验法则”或者“试试没准能行的办法”——而不是保证能产生预期结果的可重复的过程。没有任何工具和方法是用之四海皆灵的。
- 设计是自然而然形成的
设计不是在谁的头脑中直接跳出来的。它是在不断的设计评估、非正式讨论、写试验代码以及修改试验代码中演化和完善的。
5.2关键设计概念
软件的首要技术使命:管理复杂度
为了理解管理复杂度的重要性,我们有必要引用Fred Brooks 的那篇具有里程碑意义的文章——《没有银弹:软件工程中本质性和偶然性》(1987)。
- 偶然的难题和本质的难题
Brooks认为,两类不同的问题导致软件开发变得困难——本质的问题和偶然的问题。这两个术语引用了亚里士多德时代的一个哲学传统。
- 本质:本质的属性是一件事物必须具备、如果不具备就不再是该事物的属性。
- 偶然:偶然的属性则是指一件事物碰巧具有的属性,有没有这些属性都并不影响这件事物本身。
Brooks观察到,软件开发中大部分的偶然性难题在很久以前就得到解决了(软件开发不断地发展中)。
Brooks论述说,在软件开发剩下的那些本质性困难上的进展将会变得相对缓慢,究其原因,是因为从本质上说软件开发就是不断地去发掘错综复杂、相互连接的整套概念的所有细节。
其本质性的困难来自很多方面:
- 必须去面对复杂、无序的现实世界;
- 精确而完整地识别出各种依赖关系与例外情况;
- 设计出完全正确而不是大致正确的解决方案;
所有这些本质性困难的根源都在于复杂性——不论是本质的,还是偶然的。
- 管理复杂度的重要性
在对软件项目失败的原因进行调查时,人们很少吧技术原因归为项目失败的首要因素。项目的失败大多数都是不尽如人意的需求、规划和管理所导致的。但是,当项目确由技术因素导致失败时,其原因通常就是失控的复杂度。有关的软件变得极端复杂,让人无法知道它究竟是做什么的。当没人知道对一处代码的改动会对其他代码带来什么影响时,项目也就快停止进展了。
从软件架构的层次上,可以通过吧整个系统分解为多个子系统来降低问题的复杂度。人类更易于理解许多项简单的信息,而不是一项复杂的信息。所有软件设计技术的目标都是把复杂问题分解成简单的部分。子系统间的相互依赖越少,你就越容易在同一时间里专注问题的一小部分。精心设计的对象关系使关注点相互分离,从而使你能在每时每刻专注于一件事情。在更高汇聚的层次上,包提供了相同的好处。
- 如何应对复杂度
高代价、低效率的设计源于下面三种根源:
- 用复杂的方法解决简单的问题。
- 用简单但错误的方法解决复杂的问题。
- 用不恰当的复杂方法解决复杂的问题。
线面两种方法可以用来管理复杂度&#