请允许我在描述调用返回风格之前,先讲一个短小的故事。调用返回风格是在程序发展的道路上逐渐演变而来的。最初的程序设计风格,叫非结构化程序设计,是历史上最早的能够创造图灵完备算法的程序设计模式。
一个使用非结构化语言的程序中,按照顺序执行代码。它中间不存在过程,虽然可以在语句中调用子程序,但是和过程不同,这里的子程序甚至没有参数传递,还可能有好几个入口和出口,可以随意的直接跳入跳出。这些仅仅是为了反复调用一段程序提供方便,它在程序设计初期也确实提供了一些方便。然而,由于这个模式难以被编写者以外的人理解(被戏称为“意大利面条”)、需求变更可能涉及到多处代码修改并且非常难以修改(都是goto惹的祸)、难以进行部分测试,在创建大型工程方面就会存在很大的局限性。
非结构化编程的样式
由是,结构化编程应运而生。它背后是由结构化程序定理来支持的,我们先来了解一下。
结构化程序定理:只要一种编程语言可以依三个方式组合其子程序及调整控制流程,则每个可计算函数都可以用此种编程语言来表示(实现任何算法)。三个调整控制流程的方式为:
- 序列结构:按照顺序执行一个子程序,然后再执行另一个子程序
- 选择结构:根据布尔表达式的值,执行两个子程序中的一个子程序(if, else if, else);
- 迭代结构(for, while):执行子程序,直到一个布尔表达式为true
序列结构 选择结构 循环结构
我们可以看出如下几点:
- 该定理强调使用子程序、块状结构和for和while循环;而不是使用简单测试后再使用跳跃语句,例如goto语句;
- 该定理保证了只要一种编程语言支持序列结构、选择结构、循环结构(而不必使用goto语句语句),则可以实现任何可计算函数;
- 该定理是结构化编程的理论基础。
好了,现在开始进入正题。调用返回架构有几个典型的风格,分别是主程序-子程序风格和面向对象系统风格。下面我们来一一说明。
1、 主程序-子程序风格
顾名思义,它的内容就是在主程序中调用多个子程序,也就是我们常用的main函数。其中子程序还可以调用它自己的子程序,这样下来就构成一棵程序树。
程序结构示意图(其中每个箭头代表一次调用)
显而易见,它的构成组件是主程序和子程序,连接件是调用-返回机制,而拓扑结构则是层次化结构。
那怎么在实际中运用这个架构呢?结果也是简单明晰的,对于这样一个自顶向下一次到位的庞然大物,我们首先要知道整个程序中各数据的走向,所以要先画出DFD图;然后根据数据流动设计子程序部分,这样可以画出程序结构示意图(如上图);接下来就是看一看这个图究竟画得合不合理,是不是有需要改进的地方,评估一下;最后万事俱备只欠东风,就要准备写代码啦!
举个栗子??吧。
现在我们有一家医药公司,很赚钱的哦,然而赚多少钱还是取决于研发了什么药物。研发药物的投资也是非常大的,不能乱投,作为老总的你当然很想知道研发什么药品生产之后才能最赚钱,自己又懒得算(我就不信有人勤快算!),就想找程序员写一个程序计算每种药品的单位成本,通过输入药品名称来知道它研究的费用和生产的费用。
首先让我们进行第一步,画数据流图。这里给出前三层的数据流图作为参考。
第0层DFD
第1层DFD
第2层DFD
画到这里,程序的逻辑就基本清晰了。那么让我们愉快的把它翻译成程序结构图吧:)
好了,现在我们回头来审视一下这个东西。看起来没毛病。可以拿着它们去交差了!(如果忽略代码实现的话)
这个风格到这里就结束了。让我们来回顾一下。
主程序-子程序的出现在编程史上无疑是一座里程碑。它有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护,尤其在较大规模的程序设计上完全取代了非结构化编程,是一个成功的设计方法。然而主程序的正确性会受到所有子程序的影响,可谓牵一发而动全身。当程序规模过大,比如超过十万行的时候,运用它并不是一个明智的选择,因为逻辑繁复复杂,开发和测试上也都会遇到困难,它会变得特别慢并且错误百出。此外,它的可重用性几乎没有,可维护性也比较差,这意味着每一次需求变更都要修改大片大片的代码,而每一次接到任务都是一次全新的开发——哦,那可真是太糟糕了,我仿佛看到了一万个小时的无偿加班。还有一点无法忽略,就是图形用户界面的程序和所有需要实时交互的系统都很难用过程来描述,这就意味着难以开发和维护大型软件和图形界面的应用软件,从而被大众群体所接受。漏了一点(它怎么这么多缺点),由于对过程的着重关注,它忽略了对数据的控制,数据安全性一类属性也比较差。
它的优缺点都是比较明显的,使用时可以酌情考虑。
2、面向对象设计方法(Object-Oriented Design,OOD)
面向对象,顾名思义,就是关注对象之间交互的一种设计方法。什么是对象呢?从小花小草,到字符数组,凡是具有自身特定属性和自主行为的事物都可以被抽象为类,我们说这个对象就是当前类的一个实例化。在这个方法中,我们只关心对象和对象之间的交互,即只存在一个对象调用另一个对象的情况。
老虎的类定义
显而易见,它的构成组件是类和对象,连接件是对象之间的函数调用。
面向对象我不想讲太多(才不承认不是很会),学到体系结构的应该至少会一门面向对象语言,这里就略过好了??。
好,我们来总结一下。
OOD风格优点
§易维护和复用
–因为对象对其它对象隐藏它的表示(数据和操作),所以可以改变一个对象的表示,而不影响其它的对象
–类本身具有非常良好的可重用性,继承和封装方法为对象复用提供了技术支持
–组件之间依赖性降低,高内聚低耦合
§反映现实世界,是现实世界的一个映射,通俗易懂
§容易分解一个系统
–设计者可将一些数据存取操作的问题分解成一些交互的代理程序的集合
OOD风格缺点
§管理大量的对象:应该怎样确立大量对象的结构?
§继承会大量提升复杂度,关键系统中一定要慎用
§必须知道对象的身份
–为了使一个对象和另一个对象通过过程调用等进行交互,必须知道对象的标识。只要一个对象的标识改变了,就必须修改所有其他明确显式调用它的对象,并消除由此带来的一些副作用(例如,如果A使用了对象B,C也使用了对象B,那么,C对B的使用所造成的对A的影响可能是料想不到的)
§不是特别适合功能的扩展。为了增加新功能,要么修改已有的模块,要么就加入新的模块,从而影响性能
3、主程序-子程序和OOD之间的比较
前者关注系统对功能性函数的分解,程序由有一定顺序的函数集合构成。
后者关注系统对对象的抽象,程序主要依赖于数据(属性和方法)。