调用返回风格顾名思义,就是指在系统中采用了调用与返回机制。利用调用-返回实际上是一种分而治之的策略,其主要思想是将一个复杂的大系统分解为一些子系统,以便降低复杂度,并且增加可修改性。程序从其执行起点开始执行该构件的代码,程序执行结束,将控制返回给程序调用构件。
调用/返回风格架构主要包括三种具体的架构风格:主程序/子程序;面向对象风格;层次结构。
主程序/子程序软件体系结构
主程序/子程序软件体系结构是结构化开发时期的经典体系结构。这种体系结构一般采用单线程控制,把问题划分为若干处理步骤,构件即为主程序和子程序。子程序通常可合成为模块。过程调用作为交互机制,即充当连接件。调用关系具有层次性,其语义逻辑表现为子程序的正确性,取决于它调用的子程序的正确性。
主程序/子程序在主程序中调用多个子程序,也就是我们常用的main函数。其中子程序还可以调用它自己的子程序,这样下来就构成一棵程序树。

显而易见,它的构成组件是主程序和子程序,连接件是调用-返回机制,而拓扑结构则是层次化结构。
那怎么在实际中运用这个架构呢?对于这样一个自顶向下一次到位的层次结构,首先要知道整个程序中各数据的走向,先画出DFD图;然后根据数据流动设计子程序部分,这样可以画出程序结构示意图(如上图);接下来检查、评估该程序结构设计是否合理,是否有需要改进的地方。最后根据上面的程序结构示意图进行编码。
比如:有一家医药公司开发一个计算每种药品的单位成本的软件。通过输入药品名称来知道它研究的费用和生产的费用。
首先画数据流图。这里给出前三层的数据流图作为参考。

第0层的数据流图,顶层的设计。下面对中间的部分进行细化,形成下一层的数据流图。

进一步进行细化,得到带有更多细节的数据流图:

至此,整个软件的逻辑就基本清晰了。可以将上面的数据流图转换成下面的程序结构图:

主程序-子程序的出现在编程史上无疑是一座里程碑。它有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护,尤其在较大规模的程序设计上完全取代了非结构化编程,是一个成功的设计方法。然而主程序的正确性会受到所有子程序的影响。当程序规模过大,比如超过十万行的时候,运用它并不是一个明智的选择,因为逻辑繁复复杂,开发和测试上也都会遇到困难,它会变得特别慢并且错误百出。此外,它几乎没有可重用性;可维护性也比较差,这意味着每一次需求变更都要修改大片大片的代码,而每一次接到任务都是一次全新的开发。还有一点无法忽略,就是图形用户界面的程序和所有需要实时交互的系统都很难用过程来描述,这就意味着难以开发和维护大型软件和图形界面的应用软件,从而被大众群体所接受。由于对过程的着重关注,它忽略了对数据的控制,数据安全性一类属性也比较差。
面向对象的软件体系结构
抽象数据类型概念对软件系统有着重要作用,目前软件界已普遍使用面向对象系统。这种软件体系结构建立在数据抽象和面向对象的基础上,数据的表示方法和它们的相应操作封装在一个抽象数据类型或对象中。面相对象模式集数据抽象、抽象数据类型、类继承为一体,使软件工程公认的模块化、信息隐藏、抽象、重用性等原则在面向对象软体系结构中得以充分实现。
面向对象的软件体系结构的构件是对象,或者说是抽象数据类型的实例。对象是一种被称作管理者的构件,因为它负责保持资源的完整性。对象是通过函数和过程的调用来交互的。
面向对象的体系结构模式适用于数据和功能分离的系统中,同样也适合于问题域模型比较明显,或需要人机交互界面的系统,如下图所示。大多数应用事件驱动风格的系统也常常应用了面向对象风格

面向对象的软件体系结构将逻辑上的实体映射为对象,实体之间的关系映射为对象之间的应用关系。对象利用应用关系来访问对方公开的接口,完成某个特定任务;一组对象之间相互协作,完成总体目标。如下图所示的面向对象风格的抽象描述。

下面是使用面向对象软件体系结构搭建的一个软件系统的实例:

面向对象的软件体系结构的两个重要特征为:
(1)对象负责维护其表示的完整性;
(2)对象的表示对其他对象而言是隐蔽的。因为一个对象对它的客户隐藏了自己的表示,所以这些对象可以不影响它的客户就能改变其实现方法。
面向对象的系统有许多优点,并早已为人所知:
(1)高度模块性:数据与其相关操作被组织为对象, 成为模块组织的基本单位
封装功能一组功能和其实现细节被封装在一个对象中,具有功能的接口被暴露出来
(2)代码共享:对象的相对独立性可被反复重用,通过拼装形成不同的软件系统灵活性对象在组织过程中,相互关系可以任意变化,只要接口兼容
(3)易维护性:对象接近于人对问题和解决方案模型的思维方式,易于理解和修改
(4)因为对象对其他对象隐藏它的表示,所以可以改变一个对象的表示,而不影响其他的对象;
(5)设计者可将一些数据存取操作的问题分解成一些交互的代理程序的集合。
但是,面向对象的系统也存在着某些问题:
(1)为了使一个对象和另一个对象通过过程调用等进行交互,必须知道对象的标识。只要一个对象的标识改变了,就必须修改所有其他明确调用它的对象;
(2)必须修改所有显式调用它的其他对象,并消除由此带来的一些副作用。例如,如果 A 使用了对象 B,C 也使用了对象 B,那么,C 对 B 的使用所造成的对 A 的影响可能是料想不到的,如下图所示。


