万物从来不是凭空产生的?万物皆确是从无到有的。 构建电子计算机的理论基础如此,电子计算机硬件如此,编程思想如此,高级编程语言也是如此。 话说布尔提出布尔代数,香农提出在数字电路中可以实现布尔代数,冯诺依曼提出“存储程序控制”的概念。 数据和代码存储到内存,内存可寻址,控制器依次(包括跳转)读取代码,译码,产生控制信息,协调其它部件操作。 最初的程序用二进制编写,二进制使用两个符号,白茫茫的一片0和1,写、读、维护,查表(CPU抽象的指令集)都很费劲。 指令集用符号表示似乎比用0和1表示更易读、写,程序也可以用符号来写,然后再写一个汇编程序来做翻译,翻译成二进制,这就是汇编语言,相应的翻译程序叫汇编器。 更进一步抽象,叫高级语言,相应的翻译程序叫解释器或编译器。 高级语言对内存地址(一串数字,同样的不方便操作)进行命名,如变量名、常量名、函数名。 我们知道,程序的逻辑可以由减法实现,根据两数相减的结果来修改条件寄存器,通过条件和无条件跳转来实现选择和循环。 后来程序员发现,这样的跳转很容易形成无序结构,造成逻辑混乱。后来计算机科学家提出并用数学证明了用三种控制结构可以实现程序所需要的全部算法。这三种控制结构称为顺序、选择、循环结构,都只有一个入口和一个出口。三种控制结构构造函数,函数构造程序,这就是传统的面向过程,或称为结构化程序设计。
对于小问题,小工具,几十个函数能够解决问题。如果碰到大问题,大一点的系统或工具软件,如浏览器,可能需要几千个函数。就会碰到函数如何组织,如何分类的问题。C语言的做法是用文件来实现模板化。头文件提供接口,源文件提供实现,使用分享编译机制。 函数处理的数据除了局部数据以外,函数之间共享全局数据,或通过函数参数来共同定义一类结构体数据的操作集(如深度优先遍历和广度优先遍历所需要的栈和队列)。通过函数和文件来组织代码,实现模块化,但当代码规模较大时,这种函数和数据的组织方式还是无法满足代码维护和扩展的需要。
几千个函数如何分类?一种新的思维就是面向对象编程思想。将内聚性较强的一类函数和数据组织到(封装)到一起,通过继承和组合实现代码复用,通过多态实现代码扩展。 Niklaus Wirth在它的著作中提出Algorithms + Data Structures = Programs,没错,Algorithms在前,表示优先考虑,面对一个问题,先想处理方法、步骤或流程(procedure),然后是数据的表示与存储,这就是典型的结构化编程思想。与此相反,截然不同的编程思想是以数据结构为优先考量因素的面向过程,类的对象以数据为核心,成员函数是为数据服务的,一个类似的公式是,面向对象(接口)编程=对象+消息响应函数。 当然,编程设计的算法与数据结构,两者相互交替和相互影响的。面向对象与面向过程编程思维也是如此,两者相互补充,共生共存。 同时,面向对象与面向过程与任何事物一样,优劣同存。继承是面向对象的核心,但也由此容易产生代码臃肿和耦合的问题,多态同样如此(C++ STL就没有使用多态,而是使用了让算法通用于作为数据结构的容器的泛型编程思维)。对象的成员数据表示对象的状态,状态的不确定性有时也是一个问题,这也是函数式编程语言存在的理由。 大事化小,小事化了是一种解决问题的重要思维,同样的思维应用于算法称为分治,应用于程序设计称为分解。面向过程是“自顶向下,逐步求精”,就是描述问题过程的函数层层分解,细化,实现大函数由几个小函数去实现,逐步实现小函数,便实现了大函数。与面向过程不同,面向对象的分治思维是“自顶向下分解,自底向上抽象”,形成对象的层次上的继承关系、组合关系,以及由此衍生的设计模式。 自由总是由代价的, 不同的编程思维也是不同层面的约束:
-End- |
|