在前4篇的内容中,我们讨论了(以需求)驱动数据流和处理单元的算法,以及该算法处理的对象(即,处理单元的拓扑结构和处理单元间传递的数据类型)。本篇我们来讨论一下数据流与函数调用的关系,希望我们可以进一步理解这个算法的本质,以及如何在我们的设计中运用这个算法。
通常情况下,当一个函数A调用另一个函数B,那么函数B就是函数A的数据提供者,所以通常一个数据流的反向流就是一个函数调用流。而对于一组无数据交换的调用(即一个过程),我们完全可以把它们看作是一个处理单元的内部处理流程,这样我们就能(在设计程序架构时)完全忽视它们。
我们知道,程序设计的第一条原则就是“自顶向下,逐步求精”,在设计程序时,我们往往将一个大任务分解成一些小任务,然后再将一些小任务分解成更小的任务。在这个过程中,数据流图是一个非常实用的设计工具,因为它对于我们分析整个系统的业务逻辑非常的直观且容易理解,它可以将繁琐的控制逻辑(即How)屏蔽掉,而且通常,结合一个数据流图,我们可以只用几句话,就把一个任务的目标(即What和Why)说明清楚,这是使用其它设计工具所无法比拟的优势(比如UML中的类关系图,本质上,类的继承关系仍然是一种调用关系,即描述的是How)。
在我们掌握本篇文章重点讨论的算法以前,当我们完成了使用数据流图对业务逻辑的分析和设计后,我们需要通过人工的方式,将数据流逆向为函数调用流,并用特定的程序语言去实现,这个过程中,我们可能会发现一些之前没有考虑到的问题,或者需求变化了,这时候我们可能会直接修改我们的调用逻辑,而不一定会仔细分析我们的修改是否和之前设计的数据流相符,慢慢的,我们代码的实现就和原来的设计不相符了,而通常在项目进行中,我们没有很多的时间去修正我们的设计,这样,当时间久了,我们的代码就和原始设计的差距越来越远。这就是为什么很多项目的代码到了后期很难修改和维护的原因。
而“需求驱动”算法其实就是一个自动将数据流逆向为函数调用流的一个通用算法,如果我们在设计程序架构时,就有意识的引入这一算法,那么我们就不必总是通过人工方式去做这种逆向,而这种算法对整个系统的性能影响几乎可以忽略不及(因为有很多优化的方法,而且也容易帮我们找到性能的瓶颈点)。而且一旦采用了这种算法,我们的程序开发方式也会改变,现在当我们的任务目标变了,我们现在首先需要做的,就是去修改数据流图(的拓扑结构和传递的数据类型),然后简单将这种拓扑结构和数据类型的变化反应在我们的代码中,这样,既可以保证目标功能的修改,同时也有效保证了设计与实现的同步,因为我们不再需要做“数据流->调用逻辑”的转换了!
事实上,这种技术普遍的被采用于程序解释器的设计和实现中,所以如果你掌握了这种方法,你甚至可以开发一种自己的脚本语言,当然关于程序解释器的讨论已经超出了本文的范畴。
又到该结束的时候了,感谢大家对本文的关注!另外,由于本人的风格原因,我不趋向于使用源代码来解释问题,对于一些喜欢通过源代码学习的朋友表示歉意,如果你想获得本文所介绍算法的一个C++实现,可以给我留言或发邮件!