编程即理论建构
Peter Naur 因参与提出程序设计语言语法记法 “Backus-Naur Form”(BNF)而广为人知。他于 1985 年写下《编程即理论建构(Programming as Theory Building)》。这篇文章后来收入他的文集 Computing: A Human Activity(Naur 1992)。
在我看来,这篇文章最准确地说明了设计和编写程序时实际发生的事情。我在讨论应该写多少文档、如何传递默会知识(tacit knowledge),以及 XP 中设定隐喻这一练习的价值时,经常引用它。它还提供了一种考察方法论经济结构的方式。
在下面这篇文章中,请注意:设计程序员工作的质量,取决于他关于问题的理论(theory)与他关于解决方案的理论之间匹配得如何。还要注意:后来程序员工作的质量,取决于他的理论与前任程序员的理论之间匹配得如何。
借用 Naur 的思想,设计者的工作并不是传递“设计”,而是传递驱动设计的“理论”。后一个目标更有用,也更恰当。它还凸显出:理论知识寓于拥有它的人之中,具有默会性;因此,传递理论既需要传递显性知识(explicit knowledge),也需要传递默会知识。
下面是 Peter Naur 对此的表述。
“编程即理论建构”
引言
本文讨论旨在帮助理解编程是什么。它提出:恰当地说,编程应当被看作一种活动,在这种活动中,程序员形成或达到对手头事项的某种洞察,也就是一种理论。这个说法不同于一个看上去更常见的观念,即认为编程应当被看作生产一个程序以及某些其他文本。
这里所提出观点的一些背景,来自对程序以及处理这些程序的程序员团队实际情况的观察,尤其是来自那些因程序执行(program execution)或反应出乎意料、也许是错误而产生的情形,以及修改程序的场合。在编程的生产观之下,很难容纳这些观察;这表明这种观点具有误导性。理论建构观(Theory Building View)则作为另一种选择被提出。
本文还有一个更一般的背景信念:对编程是什么拥有恰当理解非常重要。如果我们的理解不恰当,就会误解这项活动中出现的困难,而我们试图克服这些困难的努力也会引发冲突和挫折。
在本文中,我会先概述若干关键背景经验。随后解释一种关于编程是什么的理论,称为理论建构观。后续各节会讨论理论建构观带来的一些后果。
编程与程序员的知识
我将使用“编程”这个词来表示设计和实现程序化解决方案(programmed solution)的整个活动。我关心的是这样一种活动:把现实世界中某项活动的某个重要部分和方面,与运行在计算机上的程序能够完成的形式符号操纵(formal symbol manipulation)相匹配。按照这种理解,我所说的编程活动自然必须包括随时间发生的发展,这种发展对应于现实世界活动(real world activity)的变化,而该活动正是由程序执行来匹配的;换句话说,它包括程序修改(program modification)。
我想表达的要点,可以这样说:在这个意义上,编程首先必须是程序员建立某种知识;这种知识基本上被视为程序员直接拥有的东西,任何文档都只是辅助性的、次要的产物。
为了给后续各节对这一观点的进一步阐述提供背景,本节余下部分将描述一些处理大型程序的真实经验。随着我反复思考这些问题,这些经验在我看来越来越重要。每个案例中的经验,要么来自我本人,要么来自与相关活动有第一手接触的人。
案例 1 涉及一个编译器。它由 A 组为语言 L 开发,并且在计算机 X 上运行得很好。现在,另一个 B 组要为语言 L + M 写一个编译器,其中 M 是对 L 的一个适度扩展,目标计算机是 Y。B 组认为 A 组开发的 L 编译器会是其设计的良好起点,于是与 A 组达成协议:A 组将提供完整文档作为支持,包括带注释的程序文本(annotated program text)、大量额外的书面设计讨论,以及个人建议。这一安排是有效的,B 组成功开发出了他们想要的编译器。在本文语境下,关键问题在于 A 组对如何实现语言扩展 M 所提供的个人建议的重要性。在设计阶段,B 组提出了若干容纳这些扩展的方案,并提交给 A 组审查。在几个主要案例中,A 组发现 B 组建议的解决方案没有利用现有编译器结构中不仅内在具有、而且在文档中已详细讨论过的设施;相反,这些方案基于对该结构的附加补丁,实际上破坏了它的能力和简洁性。A 组成员能够立刻发现这些情况,并提出简单有效、完全置于现有结构之内的解决方案。这说明,即使对于动机很强的 B 组,完整程序文本(program text)和额外文档也不足以传达设计中的深层洞察,也就是 A 组成员直接拥有的那种理论。
在这些事件之后的几年里,B 组开发的编译器由同一组织中的其他程序员接手,而没有 A 组的指导。A 组的一名成员后来获得了关于该编译器在大约 10 年进一步修改后的信息;这些信息清楚表明,到那个较晚阶段,原先强大的结构仍然可见,但已经被许多不同类型的无定形附加物弄得完全失效。因此,程序文本及其文档再次被证明不足以承载某些最重要的设计思想(design idea)。
案例 2 涉及一个大型实时系统的安装和故障诊断,该系统用于监控工业生产活动。该系统由其生产者销售,每次交付都会针对特定的传感器和显示设备环境单独适配。每次安装交付的程序规模约为 200,000 行。与这类系统处理方式有关的经验,集中在安装与排障程序员小组的角色和工作方式上。事实首先是:这些程序员从系统设计阶段开始,就在数年时间里以全职方式密切参与该系统。其次,在诊断故障时,这些程序员几乎完全依赖他们对系统随时可用的知识以及带注释的程序文本,并且想象不出还会有什么额外文档对他们有用。第三,负责特定系统安装运行的其他程序员小组,虽然从生产者人员那里获得了系统文档和使用上的完整指导,却经常遇到困难;这些困难在向生产者的安装与排障程序员咨询后,被追溯到对现有文档理解不足,但安装与排障程序员却能轻易排除。
结论似乎不可避免:至少对于某些种类的大型程序而言,对它们的持续适应、修改和错误修正,本质上依赖于一种由一组程序员拥有的知识,而这组程序员与这些程序保持着密切且持续的联系。
Ryle 的理论概念
如果承认编程必须把程序员知识的建立作为其本质部分,那么下一个问题就是更精确地刻画这种知识。这里考虑的是这样一个建议:程序员的知识应当恰当地被看作一种理论,其含义来自 Ryle [1949]。简要地说,在这个意义上,拥有某种理论的人知道如何做某些事情,并且还能用解释、论证(justification)和对询问的回答来支撑实际行动。值得注意的是,Ryle 的理论概念似乎是 K. Popper [Popper, and Eccles, 1977] 所说的非具身的世界 3 对象(unembodied World 3 objects)的一个例子,因此具有可辩护的哲学地位。在本节中,我们将更详细地描述 Ryle 的理论概念。
Ryle [1949] 在分析智识活动(intellectual activity)的性质时发展了他的理论概念,尤其关注智识活动如何区别于、并超出仅仅智能的活动。在智能行为(intelligent behaviour)中,一个人表现出来的并不是对事实的某种特定知识,而是做某些事情的能力,例如开玩笑和欣赏笑话、合乎语法地说话,或钓鱼。更具体地说,智能表现的一个特征在于人能按照某些标准(criteria)把这些事情做好;除此之外,它还表现出人能运用这些标准来发现和纠正失误、从他人的例子中学习,等等。值得注意的是,这种关于智能的概念并不依赖于这样一种观念,即智能行为取决于人遵循或服从规则、处方或方法。相反,遵循规则这一行为本身也可以或多或少地以智能方式完成;如果智能的运用依赖于遵循规则,那么就还必须有关于如何遵循规则的规则,以及如何遵循那些关于遵循规则之规则的规则,如此无穷倒退,这是荒谬的。
智识活动相对于仅仅智能的活动所具有的特征,是人建立并拥有一种理论;这里的理论被理解为一个人必须拥有的知识,使他不仅能够以智能方式做某些事情,而且能够解释它们、回答关于它们的询问、围绕它们展开论辩,等等。拥有理论的人准备好进入这些活动;在建立理论时,这个人正在努力获得它。
这里使用的理论概念不仅适用于专门研究领域中的精巧构造,也同样适用于任何受过教育的人在某些场合会参与的活动。即使是日常生活中相当朴素的活动,也可能引发人们进行理论化,例如规划如何摆放家具,或如何借助某些交通工具到达某个地方。
这里采用的理论概念明确不限于可称为洞察中最一般或最抽象的部分。例如,要拥有此处所理解的 Newton 力学理论,仅仅理解其核心定律是不够的,比如“力等于质量乘以加速度”。此外,正如 Kuhn [1970, p. 187ff] 更详细描述的,拥有该理论的人还必须理解这些核心定律如何适用于现实的某些方面,从而能够识别并把该理论应用于其他相似方面。因此,拥有 Newton 力学理论的人必须理解它如何适用于摆和行星的运动,并且必须能够识别世界中的相似现象,从而正确使用该理论中以数学表达的规则。
理论依赖于对现实世界中情境和事件之间某些相似性(similarity)的把握,这解释了为什么拥有理论的人所持有的知识原则上无法用规则表达。事实上,相关的相似性并没有、也无法用标准表达;许多其他种类的对象之间的相似性也是如此,例如人的面孔、曲调或葡萄酒的味道。
程序员要建立的理论
按照 Ryle 的理论概念,程序员必须建立的是一种理论,说明世界中的某些事务(affairs of the world)将如何由计算机程序处理,或由计算机程序来支持。在编程的理论建构观中,程序员建立的理论优先于程序文本、用户文档以及规格说明等额外文档。
在为理论建构观辩护时,基本问题是要说明:程序员因拥有理论而具有的知识,必然并且以本质方式超出了记录在文档产物中的内容。这个问题的答案是:程序员的知识至少在三个基本方面超出了文档所给出的内容:
-
拥有程序理论的程序员能够解释解决方案如何关联到它帮助处理的世界事务。这种解释必须涉及世界事务如何在整体特征和细节上,以某种意义映射到程序文本以及任何额外文档中。因此,程序员必须能够解释:程序文本的每一部分以及它的每一项整体结构特征,匹配了世界中的什么方面或活动。反过来,对于世界中的任何方面或活动,程序员也能够说明它映射到程序文本中的方式。当然,世界中的绝大多数方面和活动都会处在程序文本范围之外,因为它们在该语境下无关。然而,判定世界中的某一部分是相关的,只能由理解整个世界的人作出。这种理解必须由程序员贡献。
-
拥有程序理论的程序员能够解释为什么程序的每一部分是现在这个样子;换句话说,能够用某种论证支撑实际的程序文本。论证的最终基础始终是、也必须始终是程序员直接的、直觉性的知识(direct, intuitive knowledge)或估计。即便论证使用了推理,也许应用了设计规则、定量估计、与替代方案的比较等等,这一点仍然成立;关键在于,选择哪些原则和规则,并判断它们与手头情境相关,归根结底仍然必须是程序员直接知识的问题。
-
拥有程序理论的程序员能够建设性地回应任何修改程序的要求,使其以新的方式支持世界事务。设计如何最好地把修改纳入一个既有程序,取决于对新需求与程序中已经建立的操作设施(operational facilities)之间相似性的感知。必须感知的相似性,是世界各方面之间的相似性。它只对拥有世界知识的行动者,也就是程序员,才有意义;并且不能还原为任何有限的标准或规则集合,理由类似于前面说明为什么程序的论证不能如此还原。
本节讨论给出了采纳编程理论建构观的一些基本论据;不过,对这一观点的评估还应考虑它在多大程度上有助于形成对编程及其问题的一致理解。以下各节将讨论这些事项。
程序修改的问题与成本
提出编程理论建构观的一个重要理由,是希望建立一种关于编程的洞察,以支持对程序修改的健全理解。因此,这个问题将首先被分析。
有一点似乎人人都同意:软件会被修改。程序一旦投入运行,总会被认为只是对手头问题的一部分回答。并且,程序本身的使用也会激发出一些想法,认为程序还应提供进一步有用的服务。因此,需要处理修改的方式。
程序修改问题与编程成本问题密切相关。面对程序运行方式需要改变的情况,人们希望通过修改现有程序文本,而不是写一个全新的程序,来节省成本。
人们期待程序修改可以低成本完成,这一点需要更仔细的分析。首先要注意,这种期待无法通过与其他复杂人造构造物的修改类比来支持。对于建筑物等偶尔会付诸修改的事物,众所周知修改代价高昂,事实上,完全拆除既有建筑再新建,在经济上常常被认为更可取。其次,对低成本程序修改可能性的期待,也许来自这样一个事实:程序是保存在易于编辑的媒介中的文本。要使这种支持成立,显然必须假定主导成本是文本操作的成本。这会符合把编程看作文本生产的观念。在理论建构观下,这整套论证都是错误的。该观点并不支持这样一种期待:程序修改通常可以低成本完成。
另一个密切相关的问题是程序灵活性(program flexibility)。把灵活性纳入程序时,我们在程序中建入某些操作设施;这些设施并非立即被要求,但很可能后来会有用。因此,一个灵活的程序能够在不被修改的情况下,处理某些类别的外部环境变化。
人们常常说,程序设计应当包含大量灵活性,从而可以容易地适应变化的环境。就容易实现的灵活性而言,这样的建议或许合理。然而,一般来说,灵活性只能以相当大的成本获得。每一项灵活性都必须被设计,包括它要覆盖什么环境、应由什么样的参数控制。然后它必须被实现、测试和描述。这种成本被用来获得一个程序特性,而该特性的有用性完全取决于未来事件。显然,内建的程序灵活性并不是适应世界变化环境这一一般需求的答案。
在程序修改中,一个既有的程序化解决方案必须被改变,以适应它所要匹配的现实世界活动中的变化。在一次修改中,首先需要的是把既有解决方案与所期望修改提出的要求相对照。在这种对照中,必须确定既有解决方案的能力与新要求之间相似性的程度和种类。确定相似性的这种需要,凸显了理论建构观的价值。事实上,恰恰是在相似性的确定中,任何忽视拥有适当洞察的人直接参与这一核心要求的编程观,其缺陷会变得明显。关键在于,必须被识别的那种相似性,能够被拥有程序理论的人类把握,但完全超出规则所能确定的范围,因为甚至用来判断它的标准也无法被表述。凭借对新需求与程序已满足需求之间相似性的洞察,程序员能够设计出实现该修改所需的程序文本变更。
从某种意义上说,不存在理论修改的问题,只存在程序修改的问题。事实上,拥有理论的人必须已经准备好回应那些可能引发程序修改的问题和要求。这个观察引向一个重要结论:程序修改的问题,来自这样一种假设,即认为编程由程序文本生产(program text production)构成,而不是承认编程是一种理论建构活动。
基于理论建构观,程序文本因没有适当把握底层理论(underlying theory)的程序员所作修改而发生衰败,就变得可以理解。事实上,如果仅仅把某个期望修改看作程序文本和执行外部行为的改变,那么它通常可以用许多不同方式实现,而且这些方式全都正确。与此同时,如果相对于程序理论来看这些方式,它们可能显得非常不同:有些或许符合该理论,或以自然方式扩展它;另一些则可能与该理论完全不一致,也许具有附着在程序主体上的未整合补丁(unintegrated patches)的性质。各种变更在性质上的这种差异,只对拥有程序理论的程序员才有意义。同时,对程序文本所作变更的性质,对于程序的长期生命力(longer term viability)至关重要。为了让程序保持质量,每一次修改都必须牢固地扎根于它的理论。事实上,像简洁性和良好结构这样的质量概念,本身也只能依据程序理论来理解,因为它们是在相对于其他可能的程序文本时刻画实际程序文本的;那些其他程序文本也许能够实现同样的执行行为,但只作为程序员理解中的可能性而存在。
程序的生命、死亡与复活
编程理论建构观的一个主要主张是,任何程序的一个本质部分,也就是它的理论,根本无法表达出来,而是与人不可分割地绑定在一起。因此,在描述程序状态时,说明拥有其理论的程序员在多大程度上仍然掌管它,是很重要的。为了强调这一情况,可以把程序建构这一概念扩展为程序的生命(program life)、死亡(program death)与复活(program revival)。程序的建构,等同于程序员团队对其理论的建构,并且这种建构发生在团队之中。在程序生命期间,拥有其理论的程序员团队仍然主动控制该程序,尤其控制所有修改。当拥有其理论的程序员团队解散时,程序就死亡了。一个死去的程序可以继续在计算机中执行并产生有用结果。真正的死亡状态,会在无法有判断力地回应程序修改要求时显现出来。程序复活,就是由新的程序员团队重建其理论。
按照这些概念,程序的延续生命取决于新一代程序员接过程序理论。要让一个新程序员拥有某个程序的既有理论(existing theory),仅仅让他或她有机会熟悉程序文本和其他文档是不够的。所需要的是,这个新程序员有机会与已经拥有该理论的程序员密切共事,从而熟悉该程序在相关现实世界情境的更广泛语境中的位置,并获得关于程序如何工作、异常程序反应和程序修改如何在程序理论中被处理的知识。在既有程序理论中教育新程序员的问题,与其他活动中的教育问题十分相似;在那些活动中,关于如何做某些事情的知识支配着关于某些事情是什么情况的知识,例如写作和演奏乐器。最重要的教育活动,是学生在适当监督和指导下做相关事情。就编程而言,这种活动应当包括讨论程序与现实世界相关方面和活动之间的关系,以及程序所处理的现实世界事项的限度。
理论建构观的一个非常重要的后果是:程序复活,也就是仅仅从文档重新建立程序理论,是严格不可能的。为了避免这个后果显得不合理,可以注意到,完全死亡的程序需要复活这种情况大概很少出现,因为很难想象会把复活工作交给对原团队拥有的理论毫无认识的新程序员。即便如此,理论建构观仍强烈表明,只有在例外情形下,并且要充分意识到它充其量代价高昂、还可能导致复活后的理论(revived theory)不同于程序作者原先拥有的理论,因而可能与程序文本存在不一致时,才应尝试程序复活。
与程序复活相比,理论建构观建议,应当丢弃既有程序文本,并让新组建的程序员团队有机会重新解决给定问题。与程序复活相比,这种做法更可能产生一个有生命力的程序,而且成本不会更高,甚至可能更低。关键在于,建立一种理论去适配并支撑既有程序文本,是一项困难、令人沮丧且耗时的活动。新程序员很可能在两者之间感到撕裂:一边是对既有程序文本的忠诚,以及其中可能包含的各种晦暗和弱点;另一边是他或她必须建立的新理论,而不论好坏,这种新理论很可能会不同于程序文本背后的原有理论。
即使一个程序由不断演化的程序员团队持续保持生命,也可能出现类似问题。这些问题来自个别程序员在能力和背景经验上的差异,尤其是当团队为了维持运转而不可避免地替换个别成员时。
方法与理论建构
近年来,人们对编程方法产生了很大兴趣。本节将就理论建构观与编程方法背后的观念之间的关系作一些评论。
首先,什么是编程方法(programming method)?即便推荐某种特定方法的作者,也并不总是把这一点说清楚。这里,编程方法将被理解为一套给程序员的工作规则(work rules),告诉程序员应当做什么类型的事情、按什么顺序做、使用哪些记法或语言,以及在各个阶段产生什么类型的文档。
把这种方法概念与编程理论建构观相比,最重要的问题是行动或操作及其顺序。方法意味着这样一个主张:程序开发能够并且应当作为某些类型行动的序列来进行,每个行动都导向某种特定类型的文档化结果。在建立理论时,不可能有特定的行动序列,原因在于一个人所持有的理论并没有内在的部分划分,也没有内在顺序。相反,拥有某种理论的人能够基于它产生各种类型的陈述,以回应问题或要求。
至于使用特定类型的记法或形式化(formalization),同样只能是次要问题,因为首要之物,也就是理论,并没有、也不可能被表达,因此也就不存在其表达形式的问题。
由此可见,在理论建构观下,对于编程的首要活动而言,不存在正确的方法。
这个结论似乎在几个方面与既有看法冲突,因此也许会被看作反对理论建构观的论据。这里将讨论两个这样的表面矛盾:第一个涉及方法在科学追求中的重要性,第二个涉及实际软件开发中方法的成功。
第一个论点是:软件开发应当建立在科学方式之上,因此应当采用类似科学方法的过程。这个论点的缺陷在于假定存在一种科学方法,并且它对科学家有帮助。这个问题近年来经历了许多争论;Feyerabend [1978] 这类作者以物理学史为例,Medawar [1982] 则作为生物学家进行论证,他们的结论是:把科学方法理解为给实践科学家的一套指南,是错误的。
这一结论并不与 Polya [1954, 1957] 关于问题求解的工作相矛盾。该工作以数学领域为例,并导向同样与编程高度相关的洞察。然而,不能说它提出了一个可依之行事的方法。相反,它是一组建议,旨在通过指出可按任意顺序应用的不同工作方式,来激发问题求解者的心智活动。
第二个看似可能与理论建构观对方法的否定相矛盾的论点是:据已发表报告所述,某些特定方法的使用是成功的。对这一论点,可以这样回答:到目前为止,对编程方法有效性的、方法论上令人满意的研究似乎从未进行过。这样的研究必须采用成熟的受控实验(controlled experiments)技术(参见 [Brooks, 1980] 或 [Moher and Schneider, 1982])。缺乏这类研究的原因,一方面可以解释为:如果结果要有意义,这类调查无疑会产生很高成本;另一方面也可以解释为:在操作层面确立程序开发领域中所谓方法背后的概念,会遇到问题。多数关于此类方法的已发表报告,只是描述并推荐某些技术和过程,而没有以系统方式确立它们的有用性或有效性。C. Floyd 与数位合作者对五种不同方法进行的一项详细研究 [Floyd, 1984] 得出结论:把方法看作在任意语境中机械地导向良好解决方案的规则系统,是一种幻觉。剩下的是方法在程序员教育中的作用。这个结论完全符合编程的理论建构观。事实上,在这一观点下,程序员所建立理论的质量,在很大程度上取决于程序员对典型问题的模型解法(model solutions)、描述和验证技术,以及由许多部分以复杂交互组成的系统的结构化原则是否熟悉。因此,方法所关心的许多项目都与理论建构相关。理论建构观与方法论者(methodologists)观点的分歧,在于使用哪些技术以及按什么顺序使用这些技术的问题。在理论建构观下,这必须完全由程序员决定,并且要考虑实际要解决的问题。
程序员地位与理论建构观
理论建构观与更流行的当前观点形成最鲜明对比的领域,是程序员对这项活动的个人贡献,以及程序员应有的地位。
理论建构观与更流行观点对程序员个人贡献的看法之间的对比,在关于编程的常见讨论中处处可见。仅举一例,考虑 Oskarsson [1982] 对大型软件系统可修改性(modifiability)的研究。该研究给出了某个大型商业系统一次发布中相当多修改的广泛信息。描述覆盖了每次修改的背景、内容和实现,并特别关注程序变更如何局限在特定程序模块中。然而,其中完全没有暗示修改的实现可能取决于项目中 500 名程序员的背景,例如他们在项目上工作的时间长短;也没有说明设计决策(design decision)在这 500 名程序员之间是如何分布的。即便如此,底层理论的重要性还是在一些表述中被间接承认了,例如“决策被实现在了错误的块中”,以及提到“AXE 的一种哲学”。然而,由于研究的实施方式,这些承认只能停留为孤立的迹象。
更一般地说,当前许多关于编程的讨论似乎假定编程类似于工业生产,程序员被看作这种生产中的一个部件,一个必须受过程规则控制、并且可以轻易替换的部件。另一个相关观点是:如果人像机器一样行动,也就是遵循规则,人类就表现得最好;其结果是强调形式化表达方式,因为这使得可以用形式操纵(formal manipulation)规则来表述某些论证。这些观点与一个观念很相合;这个观念在从事计算机工作的人中似乎很常见,即认为人的心智像计算机一样工作。在工业管理层面,这些观点支持把程序员当作责任较低、只需短期教育的工人来对待。
在理论建构观下,编程活动的首要结果是程序员所持有的理论。由于这种理论按其本性是每个程序员心智拥有物的一部分,因此必须放弃把程序员视为程序生产活动中可轻易替换部件的观念。相反,程序员必须被看作一种活动的负责开发者和管理者(responsible developer and manager),而计算机是该活动的一部分。为了胜任这一位置,他或她必须获得永久职位,其地位应类似于工程师和律师等其他专业人士;这些人在企业雇员身份中的积极贡献,建立在其智识能力之上。
理论建构观所建议的程序员地位提升,必须由相应的程序员教育重新定位来支持。虽然掌握记法、数据表示和数据过程等技能仍然重要,但首要重点必须转向促进对理论形成(theory formation)的理解和才能。这在多大程度上能够被教授,仍然必须是一个开放问题。最有希望的途径,是让学生在指导下处理具体问题,并置身于主动和建设性的环境中。
结论
如果承认由外部环境变化所要求的程序修改是编程的一个本质部分,那么可以说,编程的首要目标是让程序员建立一种理论,说明手头事项如何可由程序执行来支持。这样的观点导向一种程序生命概念:程序生命取决于拥有其理论的程序员对程序的持续支持。此外,在这一观点下,把编程方法理解为程序员必须遵循的一套过程规则,是基于无效假设,因此必须被拒绝。作为这一观点的进一步后果,程序员必须被赋予负责任的、永久的开发者和管理者地位,他们所管理的是计算机作为其中一部分的活动;他们的教育必须强调理论建构的练习,同时也要获得数据处理和记法方面的知识。
References
- Brooks, R. E. Studying programmer behaviour experimentally. Comm. ACM 23(4): 207-213, 1980.
- Feyerabend, P. Against Method. London, Verso Editions, 1978; ISBN: 86091-700-2.
- Floyd, C. Eine Untersuchung von Software-Entwicklungs-Methoden. Pp. 248-274 in Programmierumgebungen und Compiler, ed H. Morgenbrod and W. Sammer, Tagung I/1984 des German Chapter of the ACM, Stuttgart, Teubner Verlag, 1984; ISBN: 3-519-02437-3.
- Kuhn, T. S. The Structure of Scientific Revolutions, Second Edition. Chicago, University of Chicago Press, 1970; ISBN: 0-226-45803-2.
- Medawar, P. Pluto’s Republic. Oxford, University Press, 1982: ISBN: 0-19-217726-5.
- Moher, T., and Schneider, G. M. Methodology and experimental research in software engineering, Int. J. Man-Mach. Stud. 16: 65-87, 1. Jan. 1982.
- Oskarsson, Ö Mechanisms of modifiability in large software systems Linköping Studies in Science and Technology, Dissertations, no. 77, Linköping, 1982; ISBN: 91-7372-527-7.
- Polya, G. How To Solve It New York, Doubleday Anchor Book, 1957.
- Polya, G. Mathematics and Plausible Reasoning. New Jersey, Princeton University Press, 1954.
- Popper, K. R., and Eccles, J. C. The Self and Its Brain. London, Routledge and Kegan Paul, 1977.
- Ryle, G. The Concept of Mind. Harmondsworth, England, Penguin, 1963, first published 1949.
应用“理论建构”
把编程看作理论建构,有助于我们理解 Extreme Programming(XP)中的“隐喻建构(metaphor building)”活动,以及默会知识和文档在传递设计知识时各自的作用。
作为理论的隐喻
Kent Beck 曾提出,对设计团队而言,把程序的一般设计简化为与单一隐喻相匹配,是有用的。例子可能是:“这个程序看起来确实像一条装配线,东西沿着生产线被添加到一个底盘上”,或者“这个程序看起来确实像一家餐馆,有服务员和菜单、厨师和收银员。”
如果隐喻是好的,设计者围绕这个隐喻产生的许多联想,就会证明适合他们的编程情境。
这正是 Naur 所说的传递设计理论。
如果“装配线”是一个恰当的隐喻,那么后来的程序员在考虑他们关于装配线所知道的内容时,会对手头软件的结构作出猜测,并发现他们的猜测是“接近的”。仅仅“装配线”这两个词就具有这样的非凡力量。
一个好隐喻的价值,会随着设计者人数的增加而增加。每个人的猜测越“接近”其他人的猜测,最终系统设计的一致性就越高。
想象 10 名程序员尽可能快速地并行工作,每个人一边做设计决策,一边添加类。每个人都会不可避免地在过程中发展出自己的理论。随着每个人添加代码,绑定他们工作的理论会变得越来越不连贯、越来越复杂。不仅维护变得更难,他们自己的工作也会变得更难。设计很容易变成一个“拼凑物(kludge)”。另一方面,如果他们拥有共同理论,就会以相互契合的方式添加代码。
一个恰当的、共享的隐喻,让人能够准确猜到团队中其他人刚刚在什么地方添加了代码,以及如何把自己的新部分与之配合起来。
默会知识与文档
文档几乎肯定落后于程序的当前状态,但人们善于四处查看。你应该在文档中放入什么?
那些能帮助下一个程序员建立关于该程序的充分理论的东西。
这一点极其重要。文档的目的,是唤起读者的记忆,建立起关于经验和隐喻的相关思维路径。
这种文档在程序生命周期中,比仅仅命名系统当前已有的部分更稳定。
设计者可以使用任何必要的表达形式来建立这些相关思路。如果他们找不到一个足以覆盖整个程序的隐喻,甚至可以使用多个隐喻。他们可能会说,一个部分实现了分形压缩算法,第二个部分像一本会计分类账,用户界面遵循模型-观察者设计模式(model-observer design pattern),如此等等。
有经验的设计者通常会从以下几项开始写文档:
-
隐喻
-
描述每个主要组件用途的文本
-
主要组件之间主要交互的图示
仅这三项,就足以让下一个团队在建构有用的设计理论上迈出很大一步。
源代码本身也向下一个程序员传达一种理论。简单、一致的命名约定(naming conventions),有助于下一个人建立关于系统的连贯理论。当人们谈论“干净代码(clean code)”时,他们很大程度上指的是读者能够多么容易地建立关于系统的连贯理论。
文档不能,也因此无需,把一切都说出来。它的目的,是帮助下一个程序员建立关于系统的准确理论。
Programming as Theory Building
1985 · Peter Naur
lucida 翻译