引言
我们为什么提出“编程是什么”这个问题,是因为我们想要解决编程的相关问题。如果问题都理解错误,那就难以解决问题了。那为什么不是提出别的问题?因为对于要讨论的主题做好定义是非常重要的,就像我们学习任何一门语言的时候,基本就是先学“我是谁”的句式。做好了定义,那就清楚接下来要用什么方法去解决问题。如果我说“我是Java程序员”,那听者肯定会在脑海里找寻“Java程序员”相关的话题或者问题。但如果我明明是Java程序员却说“我是律师”,那就是欺骗他人了。因此,“编程是什么”这个问题搞不清楚的话,就会被误导。
我们都知道“编程”指代的是编程式解决方案的整个设计和实现活动。但这定义的层次过于宏观了,就像我说“我是中国人”一样难以理解到更多的信息。下面我们将参考Peter Naur在1985年写了《Programming as Theory Building》(编程就是理论构建)一文,来详细讲解。此文被收录在他的著作集《Computing:A Human Activity》(Naur 1992)。
编程是什么,Peter Naur的答案如下:
编程就是理论构建。这句话是从解释活动的层次来看的,换句话说,就是程序员们通过编程,对手头的问题形成或达到某种见解、理论。而不是把编程当作是程序或某种其他文字材料的生产过程。
首先,我们来看“把编程当作是程序或某种其他文字材料的生产过程”的观点。试想下实际开发出来的程序(就是自己写出来的代码)和自己一开始设想的需求说明、设计等文档里面的是否一致。是不是总是要经过多次的测试和代码修复才可能达到一致?而且就算一致了,这样的一致是相对的,编程总是要考虑得更多,例如编程语言的技术限制、数据库技术的限制、硬件能力的限制等等现实的因素。非功能属性(即质量属性)的文档说明的观点更是难以与编程的结果(运行程序)相符的。因此,“把编程当作是程序或某种其他文字材料的生产过程”的观点是有问题的。接下来我们从程序修改角度来剖析“把编程当作是程序或某种其他文字材料的生产过程”的观点存在的问题。
程序修改
相信有不少人会疑惑:为什么是先从程序修改的角度?因为接受不断变化的外部环境所要求的程序修改是编程的根本部分。通过程序修改的角度来看“把编程当作是程序或某种其他文字材料的生产过程”的观点,也是一种逆推的方法,即如果编程是生产过程,那么生产过程的成果(程序代码、文档)应该能支持程序修改。就像工厂车间可以根据生产线的需要更换机器或者人员一样。下面我们引用一些真实的案例来看程序修改的问题。
【编程是什么】案例1讲是A组人员开发了一个编译器可以运行在X机器上,B组人员在这里基础上进行扩展开发使其可以运行在Y机器上,即便A组人员给B组人员提供了完整的程序文本、补充文档和A组的个人建议,并且B组人员具有很高的主动性,但B组人员并没有对设计有着深入的见解,因为A组人员发现B组所建议的解决方案摧毁了编译器原有的强大能力和简单性。又过了几年,在没有A组的指导下,另一些程序员接管了B组开发的编译器,结果它变得毫无效率了。
因此,我们可以得出这样的结论:程序代码和文档不足以作为某些最重要的设计思想的载体。
案例2讲是一个监督工业生产活动的大型实时系统的安装和故障诊断。系统的每次交付都需要去适配特殊环境的机器设备。存在全职的程序员A组一直从设计到交付都密切地关注这个系统,而且在诊断故障的时候,几乎排他地依赖这个系统上已有的知识和写好的注释代码,而不能想象有任何文档可能对他们有用。简单来说,就是利用已有知识进行排除法去诊断故障。而负责系统的特殊安装的程序员B组,尽管他们收到来自程序员A组的文档和关于使用的完全指南,但他们仍然经常遇到一些问题,需要向程序员A组咨询。原因在于程序员B组对已有文档的不充分理解,但程序员A组却可以轻易地解决这些问题。
因此,结论就是:至少对于某种大型的程序来说,持续的适应、修改和纠正其中的错误,在本质上依赖于某种知识,而只有那组紧密地持续地保持与这一系统联系的程序员才拥有这一知识。
那我们先大胆地推论:至少从程序修改的角度来看,编程必须包括构建程序员的知识,并把它作为基础的部分。
程序员的知识是理论
如果我们认同“程必须包括构建程序员的知识,并把它作为基础的部分”,那么我们应该把程序员的知识作为一个理论来正确地看待。
我们先来看:程序员的知识是什么?相信通过上面对于程序修改的讨论,我们应该清楚程序修改其实就是一种实践。既然程序修改依赖于程序员的知识,那这种知识就是实践出来的知识,也就是知道了“怎样做”。那理论是什么?理论的基本释义就是:由实践概括出来的科学知识的有系统的结论。显然,程序员的知识是理论。当然,这里关于理论的解释相当肤浅,只是为了能说明问题,更深入的关于理论的概念的理解可以参考《心的概念》(Ryle 1949)一书。
按照Ryle对理论开发出的概念,简单来说,就是:如果一个人拥有这个意义上的理论,那么他就不仅知道如何做某些事情,而且还能通过对所关心的活动进行解释、论证、回答询问,来支持实际在做的这些事情。
由程序员构建的理论表达
那既然清楚“程序员的知识是理论”,那这样的理论是否可以按照一定的规则表达出来以便于大家理解?这里先给出的答案是:在原则上不能。因为理论依赖于抓住真实世界中的情形和事件之间的某种类似性,这种依赖关系导致了某些拥有理论的人能够掌握某些知识,但在原则上他们却不能用规则的词汇把这些知识表达出来”。实际上,类似性难以使用准则的词汇表达出来,就像我们难以说出香味的类似性一样。如果我们说香味像桃子,那就不是一个准则的词汇,除非我们建立了一套用水果来形容香味的准则,就像冷热的衡量使用温度计一样。
因此,从“编程是理论构建”的观点来看,“由程序员所构建的理论”具有超过其他产品(比如程序代码、用户文档、还有规格说明等额外的文档)的地位。程序员的知识(即由程序员所构建的理论)至少在三个基本领域中超越了文档所给出的知识:
- 程序员能解释:解决方案与它能够帮助处理的现实中的事件的关系。即对于程序代码的每一部分和它整体结构上的每个特征,它匹配了现实中的哪个方面或活动。反过来,对于现实中的任何一个方面或活动,它们是怎样映射到程序代码中的。
- 程序员能解释:程序代码的每一部分的成因。最终依据一定都是程序员的直接的、直觉的知识或估计。因为根据手头相关的情况进行原则和规则的选择,必须依赖于程序员的直接知识。
- 程序员能解释:如何更好地修改程序。因为程序员拥有理论,让其可以感知到新需求或Bug修复与编程实践经验的相似性,从而可以更好地修改程序。
软件总会被修改,并期望以尽可能低成本来修改程序。如果把编程视作代码生产,则主导的成本是代码操作的成本。但在“编程是理论构建”的观点中,这种论据完全是错误的。
如果假设程序的灵活性非常高,即程序能够处理某种类型的外部环境变化,以至于根本不用任何修改,这样的话,成本就是最低的。但现实是内置在程序中的灵活性不能解决“让程序适应与现实中变化的环境”的需要。
在“编程是理论构建”的观点中,程序修改并不是理论的修改。一个拥有理论的人必须已经准备好了去响应各种可能产生程序修改的问题和要求。因为拥有理论的程序员通过洞察新需求与程序已经满足了的那些需求之间的类似性,能够更好地修改程序。
如果没有正确地领会程序所依赖的理论,程序代码就会衰退成了程序员所做的修改的结果。因为只有从“编程是理论构建”的角度,才能真正理解诸如简单和结构良好之类的代码质量。
程序修改的生命周期 我们再从程序修改的生命周期来看成本的问题。
程序修改的生命周期有生存、死亡、复活,详细解释如下:
- 程序处于生存状态就是掌握了它的理论的程序员团队保留了对于该程序的主动控制,特别是保留了对于所有修改的控制。
- 程序处于死亡状态就是掌握了它的理论的程序员团队解散了。
- 程序的复活就是新的程序员团队重新建立了这个程序的理论。
因此,从“编程是理论构建”的观点来看:在程序复活之前,应当抛弃现有的程序代码,还应当给这个新组建的程序员团队一些机会去以全新的方式解决所给的问题。这样一个过程更可能产生一个可行的程序,而不是程序复活,而成本不会更高,反而可能会更低。
即便是在用一个演进式的程序员团队来使这个程序一直保持在生存状态的时候,也可能产生类似的问题,原因是:每个程序员的能力和背景经验不同,特别是当这个团队一直在不能避免人员流动的情况下运行的时候更是如此。
程序员的定位 我们从“编程是理论构建”的观点中能学到什么呢?个人认为最重要还是程序员自己的定位问题。
编程活动的首要结果是“程序员们持有的理论”。而在真正的本质上,这一理论就是每个程序员精神财富的一部分,因此,我们必须抛弃“程序员是程序生产活动中很容易替换的部件”的概念。相反,必须把程序员看作是这一活动的一个负责任开发者和管理者,而计算机只是这个活动的一部分。为了满足这种格局,作为企业雇主,必须给程序员一个专业的定位,因为编程依赖于程序员知识上的精通;作为程序员,必须以“提高对理论构成的理解能力”为主,以“精通概念、数据表现和数据处理等技能”为辅,来提升自己的能力。
结论 编程就是一种活动。从解释活动的层次来看,编程就是理论构建。这样一种观点引出了程序修改的生命周期的概念,讨论程序复活的不可取,建议是重新构建新的理论。进一步推论是:必须把程序员定位为“有责任的永久开发者和这一活动的管理者,计算机知识其中的一部分”,而程序员的自我的提升重点在于理论构建的练习,次重点是精通概念、数据表现和数据处理等技能的学习。