4.1 模块化设计
在模块化设计中,一个软件系统被分解成相对独立的模块(module)集合。模块可以有多种形式,如类、子系统或服务。在一个理想的世界里,每个模块都是完全独立的:开发者可以在任何一个模块中工作而不知道其他模块的情况。在这个世界里,一个系统的复杂性就是其最差的模块的复杂性。
不幸的是,这种理想是无法实现的。模块必须通过互相调用对方的函数或方法来共同工作。因此,模块必须了解对方的一些情况。模块之间会有依赖关系:如果一个模块改变了,其他模块可能需要改变以配合。例如,一个方法的参数在该方法和任何调用该方法的代码之间形成了一种依赖关系。如果所需的参数改变了,那么该方法的所有调用都必须被修改以符合新的签名。依赖关系可以有很多其他的形式,而且它们可以是相当微妙的。模块化设计的目标是尽量减少模块之间的依赖关系。
为了管理依赖,我们把每个模块分为两部分:一个接口(interface)和一个实现(implementation)。接口包括在不同模块中工作的开发人员为了使用给定的模块而必须知道的一切。通常情况下,接口描述了模块做了什么(what),但没有描述它是如何(how)做到的。实现由执行接口所做承诺的代码组成。在某一特定模块工作的开发者必须了解该模块的接口和实现,以及被该模块调用的任何其他模块的接口。开发者不应该需要了解他或她正在工作的模块以外的模块的实现。
考虑一个实现平衡树的模块。该模块可能包含复杂的代码来确保树保持平衡。然而,这种复杂性对于模块的使用者来说是不可见的。用户看到的是一个相对简单的接口,用于调用操作来插入、移除和获取树中的节点。要调用一个插入操作,调用者只需要提供新节点的键和值;遍历树和分割节点的机制在接口中是不可见的。
在本书中,模块是指任何具有接口和实现的代码单元。面向对象编程语言中的每个类都是一个模块。类中的方法,或者非面向对象语言中的函数,也可以被认为是模块:这些方法或函数,每个都有一个接口和一个实现,模块化设计技术也可以应用于它们。更高级别的子系统和服务也是模块;它们的接口可能采取不同的形式,如内核调用或HTTP请求。本书中关于模块化设计的讨论大多集中在设计类上,但这些技术和概念也适用于其他类型的模块。
最好的模块是那些其接口比其实现简单得多的模块。这样的模块有两个优点。首先,一个简单的接口可以使模块对系统其他部分的复杂性降到最低。第二,如果一个模块以一种不改变其接口的方式进行修改,那么其他模块就不会受到修改的影响。如果一个模块的接口比它的实现简单得多,那么该模块的许多方面就可以被改变而不影响其他模块。
Last updated