- 内容提要
- 序 1:程序里的世界
- 序 2:最后一层表象
- 关于本书
- 致谢
- 引言:简单的本源
- 篇一:计算系统
- 第 1 章 数,以及对数据的性质的思考
- 第 2 章 逻辑
- 第 3 章 抽象
- 篇二:语言及其面临的系统
- 第 4 章 语言
- 第 5 章 从功能到系统
- 篇三:程序设计的核心思想
- 第 6 章 数据结构:顺序存储
- 第 7 章 数据结构:散列存储
- 第 8 章 执行体与它在执行过程中的环境
- 第 9 章 语法树及其执行过程
- 第 10 章 对象系统:表达、使用与模式
- 篇四:应用开发基础
- 第 11 章 应用开发的背景与成因
- 第 12 章 应用开发技术
- 第 13 章 开发视角下的工程问题
- 第 14 章 应用程序设计语言的复杂性
- 篇五:系统的基础部件
- 第 15 章 分布
- 第 16 章 依赖
- 第 17 章 消息
- 第 18 章 系统
- 篇六:系统的基本组织方法与原理
- 第 19 章 行为的组织及其抽象
- 第 20 章 领域间的组织
- 附一:主要编程范式 及其语言特性关系
- 附二:继承与混合,略谈系统的构建方式
- 附三:像大师们一样思考——从 UML 何时死掉 谈起
- 附四:VCL 已死,RAD 已死
12.3 模块化的精髓不在于外在形式的分离,而在于内在逻辑的延续
图 32 展示了在稍早一些的应用开发语言中,从“代码的粒度”出发的抽象概念。
其中,单元或模块用于组织一系列函数,而一个应用 4 则是由单元或模块构成。在这样的体系中,“化整为零”的问题会变得相对简单,即如何有规则或有逻辑地将一堆函数组织成单元。这里的“规则”与“逻辑”阐述了组织法则的两个方向。
其一,我们可以设定一个简单的分类依据,使得位于同一个单元中的函数表现出一定的相似性。例如开发一个图形库,我们可以将与图形设备相关的功能放在 device 库中,将绘制功能放在 graph 库中,将渲染功能放在 render 库中,将与图形库无关但又与计算机基础环境相关的功能放在 base 库中,如此等等。最后,我们将一些杂乱无章的功能放在 misc 库中。请注意,这一切的分类依据是“功能的归属与使用者”。类似地,我们也可以依据数据的位置来建立分类依据。例如同样是这个图形库,我们可以将基础数据运算放在 bits 库中,并基于此建立关于图形运算的类型抽象库 types。接下来我们定义在不同设备上适用的数据结构,比如在存储设备中的种种文件格式 fileTypes、在内存中复制和运算的 dibs(设备无关位图,Device-Independent Bitmap)以及在某种具体显示设备中适用的 cudaTypes(CUDA,Compute Unified Device Architecture)等。这样依据数据(所处的)位置以及需要进行的计算进行分类,也便于将数据及其副本放在不同的环境下开发。
这一类的方案或试图交付一个可以被使用甚至被共用的功能集,或通过抽取不同层次(例如面向不同设备或不同场景)的代码,使之可以“或多或少”应用于不同的环境。与这个组织原则密不可分的一个问题是:如何使一个“单元/模块”向外公布它所具有的功能集。这形成了著名的“开放细节”与“公开功能但隐藏细节”之争 5 ,如今后者已成为应用接口设计思想的主流,前者则部分地影响并推进了开放源代码这一思想。
但总的来说,这个组织法则只解决了一个应用中能被静态规则化的部分。无论如何,它无法满足“让程序运行起来”之后可能带来的种种变化。
其二,我们可以使得一个单元或多个单元中的函数存有某种逻辑关系。著名的“自顶向下程序设计”的思想,就处于这一组织法则所代表的方向上。例如:
设有一个逻辑(X),功能是将 m 变换为 n,如图 33 所示;
图 33 基本功能:将 m 变换为 n
由于 X 的规模巨大,我们将它分成三个逻辑步骤(顺序逻辑 1→2→3)来实现,如图 34 所示。
图 34 分解:三个步骤
虽然向下一层的分解并不限定各步骤之间的关系,但我们注意到此前讨论过的一个事实,即所有逻辑都可以被理解为顺序逻辑中的一个步骤。也就是说,步骤 2 依赖于步骤 1,步骤 3 依赖于步骤 2。随后我们继续向下一层分解,如图 35 所示。
图 35 持续分解:更多的步骤、逻辑或子系统
在图 35 中:
- 步骤 1 的子步骤 1.1 分成了 1.2 和 1.3 两个分支,最终以 1.3 为出口,传出数据 n’;
- 步骤 2 则被分解为三个子步骤的循环,并总是以步骤 2.3 为出口,传出数据 n“;
- 步骤 3 分解为两个顺序的子步骤,得到转换结果 n。
由此无论是针对顶层 X 这个逻辑,还是针对第二、三层各分解的逻辑,整体的逻辑关系都没有变化。所有的逻辑关系在函数与函数之间,以及在“一堆函数”与“另一堆函数”之间都可以被简单地抽象为“顺序依赖”。即使将这些单元/模块之间的关系映射到最终子系统的划分之上,这种逻辑关系也不会变化。例如从子系统划分上看:
- 子系统 1 被设计为“预处理器”(preProcessor);
- 子系统 2 被设计为“分析器”(analyzer)或“过滤器”(filter);
- 子系统 3 被设计为(下一阶段的)“数据供应器”(dataProvider)。
这三个子系统以及它们的组织关系就可以构成某种数据处理系统的、整体的、面向运行期的逻辑架构。
进一步地说,通常单元/模块之间的逻辑关系只是简单的依赖关系。这一关系足以支撑由结构化程序设计带来的计算需求,包括支持数据流转与逻辑执行。 6
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论