模块化:编程中如何处理对象和流
1 语言模块化
为什么需要模块化?
如果语言只支持过程和数据组合以构建复合实体,这些工具不足以设计程序。
因为我们了解到抽象对于帮助应对大型系统的复杂性是至关重要的。
有效的程序综合还需要可以指导我们制定程序总体设计的组织原则。
特别是,我们需要一些策略来帮助我们构建大型系统,以便它们保持模块化的,也就是说,它们可以“自然地”划分为可以单独开发和维护的连贯部分。
这是一种强大的设计策略,特别适用于建模物理系统的程序,是将我们的程序结构建立在被建模 系统的结构上。
对于系统中的每个对象,我们构造一个对应的计算对象。对于每个系统动作,我们在计算模型中定义了一个符号操作。
我们希望使用这种策略来扩展模型以适应新对象或新动作,不需要对程序进行战略性更改,只需添加这些对象或动作的新符号类比即可。
如果我们在系统组织中取得了成功,那么要添加新功能或调试旧功能,我们将只需要处理系统的本地化的模块部分。
那么,在很大程度上,我们组织大型程序的方式取决于我们对要建模的系统的看法。
这里有由系统结构的两种截然不同的“世界观”产生的两种突出的组织策略。
第一个认为世界是离散的,组织战略专注于对象,
将大型系统视为行为可能随时间变化的不同对象的集合。
另一种组织认为世界是连续的,因此组织战略专注于流,
在系统流,就像一个电气工程师观看的信息信号处理系统。
基于对象的方法和流处理方法都在编程中提出了重要的语言问题。
对于对象,我们必须关注计算对象如何改变并保持其身份。这将迫使我们放弃旧的替代计算模型 ,转而采用更机械但理论上不易处理的计算的环境模型。
处理对象、变化和身份的困难是需要在我们的计算模型中应对时间流的基本困难。
当我们允许并发执行程序时,这些困难会变得更大。
当我们将模型中的模拟时间与评估期间计算 机中发生的事件顺序分离时,可以最充分地利用流方法。我们将使用一种称为惰性计算的方法。
1.1 认识流和对象
物理学家有时会采用这种观点,引入粒子的“世界线”作为推理运动的工具。
这是考虑信号处理系统 的自然方式。
在物理学中,当我们观察一个移动的粒子时,我们说该粒子的位置(状态)正在改变。但是,从粒子的角度来看世界时空线没有 任何变化
流处理的部分力量在于它让我们可以忽略事件在我们的程序中实际发生的顺序。不幸的是,这正是 我们在有任务的场景下无法承受的,这迫使我们关注时间和变化。
对于任何两个流,通常有不止一个 可接受的交错顺序。
因此,从技术上讲,“合并”是一个关系而不是一个函数——答案不是输入的确定性函数。
我们已经提到在处理并发时非确定性是必不可少的。
从功能的角度来看,合并关系说明了相同的基本不确定性。以后将从另一个角度来看非确定性。
对象模型通过将世界分成不同的部分来近似世界。
功能模型不沿对象边界模块化。
对象模型在以下场景下很有用:
“对象”的非共享状态比它们共享的状态大得多。
对象视点失败的地方的一个例子是量子力学,将事物视为单个粒子会导致悖论和混乱。
将对象视图与功能视图统一起来可能与编程无关,而与基本的认识论问题有关。
1.2 赋值和状态
很多人通常将世界视为由独立的对象组成,每个对象都有随时间变化的状态。
如果一个对象的行为受其历史影响,则称其为“具有状态”。
例如,你的银行账户的状态是,“我可以提取 100 元吗?”
这个问题的答案取决于存款和取款交易的历史。
我们可以通过一个或多个来表征一个对象的状态状态变量,其中包含足够的历史信息来确定对象的当前行为。
在一个简单的银行系统中,我们可以通过当前余额而不是记住账户交易的整个历史来表征账户的状态。
在由许多对象组成的系统中,对象很少是完全独立的。每个都可以通过交互影响其他对象的 状态,交互作用将一个对象的状态变量耦合到其他对象的状态变量。
事实上,当系统的状态变量可以被分组到仅与其他子系统松散耦合的紧密耦合的子系统中时,系统由单独的对象组成的观点是最有用的。
这种系统视图可以成为组织系统计算模型的强大框架。对于这样一个模块化的模型,它应该 被分解为模拟系统中实际对象的计算对象。
2 小结
每个计算对象必须有自己的局部状态变量描述实际对象的状态。由于被建模系统中对象的状态会随时间变化,因此相应计算对象的状态变量也必须发生变化。
如果我们选择通过计算机中经过的时间对系统中的时间流进行建模,那么我们必须有一种方法来构造计算对象,其行为会随着我们的程序运行而改变。
特别是,如果我们希望通过编程语言中的普通符号名称对状态变量进行建模,那么该语言必须提供一个赋值运算使我们能够更改与名称关联的值。
- 点赞
- 收藏
- 关注作者
评论(0)