2.3 造成复杂性的原因
Last updated
Last updated
现在你知道了复杂性的高层次(high-level)症状以及为什么复杂性会使软件开发变得困难,下一步就是要了解是什么导致了复杂性,这样我们就可以设计系统来避免这些问题。复杂性是由两件事引起的:依赖(dependency)和模糊性(obscurity)。本节从高层次上讨论了这些因素;后续章节将讨论它们与低层次设计决策的关系。
就本书的目的而言,当一段给定的代码不能被孤立地理解和修改时,就存在依赖;该代码以某种方式与其他代码相关,如果给定的代码被改变,就必须考虑和/或修改其他代码。在图2.1(a)的网站例子中,背景颜色在所有的页面之间产生了依赖。所有的页面都需要有相同的背景,所以如果一个页面的背景被改变,那么所有的页面都必须被改变。另一个依赖关系的例子发生在网络协议中。通常情况下,协议的发送方和接收方有单独的代码,但它们必须各自符合协议;改变发送方的代码几乎总是需要在接收方进行相应的改变,反之亦然。一个方法的签名在该方法的实现和调用该方法的代码之间建立了一种依赖关系:如果一个新的参数被添加到一个方法中,该方法的所有调用必须被修改以指定该参数。
依赖是软件的一个基本部分,不能完全消除。事实上,我们有意引入依赖作为软件设计过程的一部分。每次你写一个新的类时,你都会围绕该类的API创建依赖关系。然而,软件设计的目标之一是减少依赖关系的数量,并使剩下的依赖关系尽可能地简单和明显。
考虑一下网站的例子。在旧的网站上,每个页面的背景都是单独指定的,所有的网页都是相互依赖的。新的网站解决了这个问题,它在一个中心位置指定了背景颜色,并提供了一个API,各个页面在渲染时使用这个API来读取背景颜色。新的网站消除了页面之间的依赖,但它围绕着读取背景颜色的API创造了一个新的依赖。幸运的是,新的依赖关系更加明显:显然,每个独立的网页都依赖于bannerBg
的颜色,开发者可以通过搜索它的名字轻松地找到所有使用该变量的地方。此外,编译器有助于管理API的依赖:如果共享变量的名称改变了,任何仍然使用旧名称的代码都会发生编译错误。新网站用一个更简单、更明显的依赖关系取代了一个不明显的、难以管理的依赖关系。
复杂性的第二个原因是模糊性。当重要的信息不明显时,就会出现模糊不清的情况。一个简单的例子是,一个变量的名字是如此地通用,以至于它没有携带多少有用的信息(例如,time
)。或者,一个变量的文档可能没有说明它的单位,所以唯一的方法就是扫描代码中使用该变量的地方来找出答案。模糊性通常与依赖有关,即依赖的存在并不明显。例如,如果在一个系统中增加了新的错误状态,可能需要在一个为每个状态保存字符串信息的表中增加一个条目,但对于一个看状态声明的程序员来说,信息表的存在可能并不明显。不一致性也是造成模糊性的一个主要原因:如果同一个变量名被用于两个不同的目的,那么对于开发者来说,某个特定的变量是为哪个目的而服务的就不明显。
在许多情况下,模糊性的产生是由于不够完备的文档;讨论了这个话题。然而,模糊性也是一个设计问题。如果一个系统有一个干净而易于理解的设计,那么它需要的文档就会少一些。对大量文档的需求往往是一个危险信号,表明设计不太正确。减少模糊性的最好方法是简化系统设计。
依赖和模糊性共同构成了中描述的复杂性的三种表现形式。依赖导致了变更放大和高认知负荷。模糊性造成了未知的未知因素,也造成了认知负荷。如果我们能够找到将依赖和模糊性最小化的设计技术,那么我们就可以降低软件的复杂性。