第 3 章

模块化、对象和状态

M

(纵然变化,它仍屹立。)

赫拉克利特

Plus ça change, plus c'est la même chose.
(变得越多,越是不变。)

阿方斯·卡尔

前面的章节介绍了构成程序的基本元素。我们看到了如何将基本过程和基本数据组合起来构造复合实体,并且学到了抽象在帮助我们应对大型系统复杂性方面至关重要。但这些工具对于设计程序来说还不够。有效的程序综合还需要组织原则,这些原则可以指导我们构建程序的整体设计。特别地,我们需要策略来帮助我们构建大型系统,使其成为模块化的,也就是说,能够被"自然地"划分为内聚的部分,这些部分可以独立开发和维护。

一个强大的设计策略,特别适用于构建物理系统的建模程序,就是将程序的结构建立在被建模系统的结构之上。对于系统中的每个对象,我们构造一个对应的计算对象。对于每个系统动作,我们在计算模型中定义一个符号操作。使用这种策略,我们希望扩展模型以适应新对象或新动作时,不需要对程序进行战略性的更改,只需要添加这些对象或动作的新符号对应物。如果我们成功地组织了系统,那么添加新特性或调试旧特性时,我们只需要在系统的局部部分上工作。

在很大程度上,我们组织大型程序的方式是由我们对要建模的系统的认知所决定的。在本章中,我们将研究两种显著的组织策略,它们源于两种相当不同的系统结构的"世界观"。第一种组织策略集中于对象,将大型系统视为不同对象的集合,这些对象的行为可能随时间变化。另一种组织策略集中于在系统中流动的信息流,就像电气工程师看待信号处理系统一样。

基于对象的方法和流处理方法都在编程中引发了重要的语言问题。对于对象,我们必须关注计算对象如何能够变化同时又保持其身份。这将迫使我们放弃旧的替代计算模型(第1.1.5节),转而采用一种更机械但理论上较难处理的环境计算模型。处理对象、变化和身份的困难,是在计算模型中必须处理时间问题的基本后果。当我们允许程序并发执行的可能性时,这些困难变得更加严重。当我们将模型中的模拟时间与求值过程中计算机内事件发生的顺序解耦时,流方法可以得到最充分的利用。我们将通过一种称为延迟求值的技术来实现这一点。