没有银弹Word下载.docx
《没有银弹Word下载.docx》由会员分享,可在线阅读,更多相关《没有银弹Word下载.docx(19页珍藏版)》请在冰点文库上搜索。
![没有银弹Word下载.docx](https://file1.bingdoc.com/fileroot1/2023-5/4/8e74a5fc-96ee-4a3f-aa40-cac090644747/8e74a5fc-96ee-4a3f-aa40-cac0906447471.gif)
不仅仅是在目力所及的范围内没有发现银弹,软件的特性本身也导致不大可能有任何的发明创新——能够像计算机硬件工业中的电子器件、晶体管、大规模集成一样——提高软件的生产率、可靠性和简洁程度。
我们甚至不能期望每两年有两倍的增长。
首先,我们必须看到这样的畸形并不是由于软件发展得太慢,而是因为计算机硬件发展得太快。
从人类文明开始,没有任何其他产业技术的性价比,能在30年之内取得6个数量级的提高,也没有任何一个产业可以在性能提高或者成本降低方面取得如此的进步。
这些进步来自计算机制造产业的转变,从装配工业转变成流水线工业。
其次,让我们了解中间的困难,来看看我们能期待什么样的软件技术产业发展速度。
效仿亚里士多德,我将它们分成根本的(essence)——软件特性中固有的困难,次要的(accident)——出现在目前生产中,但并非那些与生俱来的困难。
我们在下一节中讨论次要问题。
首先,来关注根本问题。
一个相互牵制关联的概念结构是软件实体必不可少的部分,它包括:
数据集合、数据条目之间的关系、算法和功能调用等等。
这些要素本身是抽象的,体现在不同的表现形式下的概念构造是相同的。
尽管如此,它仍然是内容丰富和高度精确的。
我认为软件开发中困难的部分是规格说明、设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程度进行验证。
当然,我们还是会犯一些语法错误,但是与绝大多数系统中的概念错误相比,它们是微不足道的。
如果这是事实,软件开发总是非常困难的,天生就没有银弹。
让我们来考虑现代软件系统中这些无法规避的内在特性:
复杂度、一致性、可变性和不可见性。
复杂度。
规模上,软件实体可能比任何由人类创造的其他实体更复杂,因为没有任何两个软件部分是相同的(至少在语句的级别上)。
如果有相同的情况,我们会把它们合并成供调用的子函数。
在这个方面,软件系统与计算机、建筑或者汽车大不相同,后者往往存在着大量重复的部分。
数字计算机本身就比人类建造的大多数东西复杂。
计算机拥有大量的状态,这使得构思、描述和测试都非常困难。
软件系统的状态又比计算机的状态多若干个数量级。
同样,软件实体的扩展也不仅仅是相同元素重复添加,而必须是不同元素实体的添加。
大多数情况下,这些元素以非线性递增的方式交互,因此整个软件复杂度要比非线性增长多得多。
软件的复杂度是根本属性,不是次要因素。
因此,抽掉复杂度的软件实体描述常常也去掉了一些本质属性。
数学和物理学在过去三个世纪取得了巨大的进步,数学家和物理学家们为复杂的现象建立了简化的模型,从模型中抽取出各种特性,并通过试验来验证这些特性。
这些方法之所以可行,是因为模型中忽略的复杂度不是被研究现象的根本属性。
当复杂度是本质特性时,这些方法就行不通了。
上述软件特有的复杂度造成了很多经典的软件产品开发问题。
由于复杂度,团队成员之间的沟通非常困难,导致了产品瑕疵、成本超支和进度延迟;
由于复杂度,使列举(还远远不是理解)所有可能的状态变得十分困难,影响了产品的可靠性;
由于函数的复杂度,函数调用变得困难,导致程序难以使用;
由于结构性复杂度,程序难以在不产生副作用的情况下用新函数扩充;
由于结构性复杂度,造成很多安全机制状态上的不可见性。
复杂度不仅仅导致技术产生困难,还引发了很多管理上的问题。
它使全面理解问题变得困难,从而妨碍了概念上的完整性;
它使所有离散出口难以寻找和控制;
它引起了大量学习和理解上的负担,使开发慢慢演变成了一场灾难。
一致性。
并不是只有软件工程师才面对复杂度问题。
物理学家甚至在非常“基础”的级别上,也会面对异常复杂的事物。
不过,物理学家坚信,必定存在着某种通用原理,或者在夸克中,或者在统一场论中。
爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专横武断或反复无常的。
软件工程师却无法从类似的信念中获得安慰,他必须掌握的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。
它们随接口的不同而改变,随时间的推移而变化,而且,这些变化不是必需的,仅仅由于它们是不同的人——而非上帝——设计的结果。
许多情况下,因为是开发最新的软件,它必须遵循各种接口。
另一些情况下,软件的开发目标就是兼容性。
在上述的所有情况中,很多复杂性来自保持与其他接口的一致方面,对软件的任何再设计,都无法简化这些复杂特性。
可变性。
软件实体经常会遭受到持续的变更压力。
当然,建筑、汽车、计算机也是如此。
不过,工业制造的产品在出厂之后不会经常地发生修改,它们会被后续模型所取代,或者必要更改会被整合到具有相同基本设计的后续产品系列中。
汽车的更改十分罕见,计算机的现场调整有时发生。
然而,与软件的现场修改比起来,它们都要少很多。
其中的部分原因是因为系统中的软件包含了很多功能,而功能是最容易感受变更压力的部分。
另外的原因是因为软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展。
日常生活中,建筑有可能发生变化,但众所周知,建筑修改的成本很高,从而打消了那些想提出修改的人的念头。
所有成功的软件都会发生变更。
现实工作中,经常发生两种情况。
当人们发现软件很有用时,会在原有应用范围的边界,或者在超越边界的情况下使用软件。
功能扩展的压力主要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们。
其次,软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比当初开发软件所用的计算机硬件平台要长。
即使不是更换计算机,也有可能是换新型号的磁盘、显示器或者打印机。
软件必须与各种新生事物保持一致。
简言之,软件产品扎根于文化的母体中,如各种应用、用户、自然及社会规律、计算机硬件等等。
后者持续不断地变化着,这些变化无情地强迫着软件也随之变化。
不可见性。
软件是不可见的和无法可视化的。
例如,几何抽象是强大的工具。
建筑平面图能帮助建筑师和客户一起评估空间布局、进出的运输流量和各个角度的视觉效果。
这样,矛盾变得突出,遗漏的地方可以捕捉到。
同样,机械制图、化学分子模型尽管是抽象模型,但都起了相同的作用。
总之,都可以通过几何抽象来捕获物理存在的几何特性。
软件的客观存在不具有空间的形体特征。
因此,没有已有的几何表达方式,就像陆地海洋有地图,硅片有膜片图,计算机有电路图一样。
当我们试图用图形来描述软件结构时,发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。
这些图形可能代表控制流程、数据流、依赖关系、时间序列和名字空间的相互关系等等。
它们通常不是有较少层次的扁平结构。
实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层次化一个或多个图形。
[2]
除去软件结构上的限制和简化方面的进展,软件仍然保持着无法可视化的固有特性,从而剥夺了一些具有强大功能的概念工具的构造创意。
这种缺憾不仅限制了个人的设计过程,也严重地阻碍了思路相互之间的交流。
以往解决次要困难的一些突破
如果回顾一下软件领域中取得的最富有成效的三次进步,我们会发现每一次都是解决了软件构建上的巨大困难,但是这些是次要困难,不是本质属性,也不是主要困难。
同样,我们可以对每一次进步进行外推,来了解它们的固有限制。
高级语言。
毋庸置疑,软件生产率、可靠性和简洁性上最有力的突破是使用高级语言编程。
大多数观察者相信开发生产率至少提高了5倍,同时可靠性、简洁性和理解程度也大为提高。
那么,高级语言取得了哪些进展呢?
它减轻了一些次要的软件复杂度。
抽象程序包含了很多概念上的要素:
操作、数据类型、流程和相互通讯,而具体的机器语言程序则关心位、寄存器、条件、分支、通道、磁盘等等。
高级语言所达到的抽象程度体现了抽象程序所需要的要素,避免了更低级的元素,它消除了并不是程序所固有的整个级别的复杂度。
高级语言最可能实现的是为所有编程人员在抽象程序中提供想到的要素。
可以肯定的是,我们思考数据结构、数据类型和操作的复杂程度稳固提高,只不过是以非常缓慢的速度。
另外,程序开发方法越来越接近用户的复杂度。
然而,对于较少使用那些复杂深奥语言要素的用户,高级语言在某种程度上增加而不是减少了脑力劳动的负担。
分时。
大多数观察者相信,分时在提高程序员的生产率和产品质量方面起到了很大作用,尽管它产生的进步不如高级语言。
分时解决了完全不同的困难,保证了及时性,从而使我们能维持对复杂度的一个总体把握。
批处理编程的较长周转时间意味着不可避免会遗忘一些细枝末节。
如果我们停下编程,调用编译程序或者执行程序,思维上的中断使我们不得不重新进行思考,它在时间上的代价非常高昂。
最严重的结果可能是失去对复杂系统的掌握。
较长的周转时间和机器语言的复杂度一样,是软件开发过程的次要困难,而不是本质困难。
这直接导致分时作用非常有限。
主要效果缩短了系统的响应时间。
随着它接近于零,超过人类可以辨识的基本能力——大概100毫秒时,所获得的好处就接近于零了。
统一编程环境。
第一个集成开发环境——Unix和Interlisp,现在已经得到了广泛应用,并且由于整合的因素使生产率提高。
为什么?
它们主要通过提供集成库、统一文件格式、管道和过滤器,解决了共同使用程序的次要困难。
这样,概念性结构理论上的相互调用、提供输入和互相使用,在现实中可以非常容易地实现。
因为每个新工具可以通过标准格式在任何一个程序中应用,这种突破接着又激发整个工具库的开发。
由于这些成功,环境开发是当今软件工程研究的主要题目。
我们将在下节讨论期望达到的目标和限制。
银弹的希望
现在,让我们来讨论一下当今可能作为潜在银弹的最先进的技术进步。
它们各自针对什么样的问题?
它们是属于必要问题,或者依然是解决我们剩下的次要困难?
它们是提供了创新,还是仅仅是增量改进?
Ada和其他高级编程语言。
近来,最被吹捧的开发进展之一是编程语言Ada,一种20世纪80年代的高级语言。
Ada实际上不仅仅反映了语言概念上的突破性进展,而且蕴涵了鼓励现代设计和模块化概念运用的重要特性。
由于Ada采用的是抽象数据类型、层次结构的模块化理念,所以Ada理念可能比语言本身更加先进。
Ada使用设计来承载需求,作为这一过程的自然产物,它可能过于丰富了。
不过,这并不是致命的,因为它的词汇子集可以解决学习问题,硬件的进展能提供更高的MIPS(每秒百万指令集),减少编译的成本。
软件系统结构化的先进理念实际上非常好地利用了MIPS上的进展。
20世纪60年代,曾在内存和循环成本上广受谴责的操作系统,如今已被证明是一种能使用某些MIPS和廉价内存的非常优秀的系统。
然而,Ada仍然不是消灭软件生产率怪兽的银弹。
毕竟,它只是另一种高级语言,这类语言最大的回报来自出现时的冲击,它通过使用更加抽象的语句来开发,降低了机器的次要复杂度。
一旦这些难题被解决,就只剩下非常少的问题了,解决剩余部分的获益必然也要少一些。
我预言,在以后的十年中,当Ada的效率被大家评估认可时,它会产生相当大的变化,但并不是因为任何特别的语言特性,不是由于这些语言特性被合并在一起,也不是因为Ada开发环境会不断发展进步。
Ada的最大贡献在于编程人员培训方式的转变,即对开发人员需要进行现代软件设计技术培训。
面向对象编程。
软件专业的一些学生坚持面向对象编程是当今若干新潮技术中最富有希望的。
[3]我也是其中之一。
达特茅斯的MarkSherman提出,必须仔细地区别两个不同的概念:
抽象数据类型和层次化类型,后者也被称为类(class)。
抽象数据类型的概念是指对象类型应该通过一个名称、一系列合适的值和操作来定义,而不是理应被隐藏的存储结构。
抽象数据类型的例子是Ada包(以及私有类型)和Modula的模块。
层次化类型,如Simula-67的类,是允许通用接口的定义可以被后续子类型精化的。
这两个概念是互不相干的——可以只有层次化,没有数据隐藏;
也可能是只有数据隐藏,而没有层次化。
两种概念都体现了软件开发领域的进步。
它们的出现都消除了开发过程中的非本质困难,允许设计人员表达自己设计的内在特性,而不需要表达大量句法上的内容,这些内容并没有添加新的信息。
对于抽象数据类型和层次化类型,它们都是解决了高级别的次要困难并允许采用较高层次的表现形式来表达设计。
不过,这些提高仅仅能消除所有设计表达上的次要困难。
软件的内在问题是设计的复杂度,上述方法并没有对它有任何的促进。
除非我们现在的编程语言中,不必要的低层次类型说明占据了软件产品设计的90%,面向对象编程才能带来数量级上的提高。
对面向对象编程这颗“银弹”,我深表怀疑。
人工智能。
很多人期望人工智能上的进展可以给软件生产率和质量带来数量级上的增长,[4]但我不这样认为。
究其原因,我们必须剖析“人工智能”意味着什么,以及它是如何应用的。
Parnas澄清了术语上的混乱:
现在有两种差异非常大的AI定义被广泛使用。
AI-1:
使用计算机来解决以前只能通过人类智慧解决的问题。
AI-2:
使用启发式或基于规则的特定编程技术。
在这种方法中,对人类专家进行了研究,判断他们解决方法的启发性思维或者经验法则……程序被设计成以人类解决问题的方式来运作。
第一种定义的意义容易发生变化……今天可能适合AI-1定义的程序,一旦我们了解了它的运行方式,理解了问题,就不再认为它是人工智能……遗憾的是,我无法识别这个领域的特定知识体系……绝大多数工作是针对问题域的,我们需要一些抽象或者创造性来解决上述问题。
[5]
我完全同意这种批评意见。
语音识别技术与图像识别技术的共同点非常少,它们又都与专家系统中应用的技术不同。
例如,我觉得很难去发现图像识别技术能给编程开发实践带来什么样的差异。
同样,语音识别也差不多——软件开发上的困难是决定说什么,而不是如何说。
表达的简化仅仅能提供少量的促进作用。
至于AI-2专家系统技术,应该用专门的章节来讨论。
专家系统。
人工智能领域最先进的、被最广范使用的部分,是开发专家系统的技术。
很多软件科学家正非常努力地工作着,想把这种技术应用在软件的开发环境中。
[6]那么它的概念是什么,前景如何?
专家系统是包含归纳推论引擎和规则基础的程序,它接收输入数据和假设条件,通过从基础规则推导逻辑结果,提出结论和建议,向用户展示前因后果,并解释最终的结果。
推论引擎除了处理推理逻辑以外,通常还包括复杂逻辑或者概率数据和规则。
对于解决相同的问题,这种系统明显比传统的程序算法要先进很多。
●推论引擎技术的开发独立于应用程序,因此可以有多种用途。
在该引擎上付出较大的力气是很合理的。
实际上,这种引擎技术非常先进;
●基于应用的、可变更的部分,在基础规则中以一种统一的风格编码,并且为规则基础的开发、更改、测试和文档化提供了若干工具。
这实际上对一些应用程序本身的复杂度进行了系统化。
EdwardFeigenbaum指出,这种系统的能力不是来自某种前所未有的推导机制,而是来自非常丰富的知识积累基础,这一基础更加精确地反映了现实世界。
我认为这种技术提供的最重要进步是具体应用的复杂度与程序本身相分离。
如何把它应用在软件开发工作中呢?
可以通过很多途径:
建议接口规则、制定测试策略、记录各种bug产生的频率和提供优化建议,等等。
例如,考虑一个虚构的测试顾问系统。
在最根本的级别,诊断专家系统和飞行员的检查列表很相似,对困难可能的成因提供基本建议。
建立基础规则时,可以依据更多的复杂问题征兆报告,从而使这些建议更加精确。
可以想象,一种调试辅助程序起初提供的是一般化建议,随着基础规则包括越来越多的系统结构信息,它产生的推测和推荐的测试也越来越准确。
这种类型的专家系统可能与传统系统彻底分离,系统中的规则基础可能与相应的软件产品具有相同的层次模块化结构,因此当对产品进行模块化修改时,诊断规则也能相应地进行模块化修改。
产生诊断规则也是在为模块和系统编制测试用例集时必须完成的任务。
如果它以一种适当通用的方式来完成,对规则采用一致的结构,拥有一个良好可用的推测引擎,事实上它就可以减少测试用例设计的总体工作量,并帮助整个软件生命周期的维护和修改测试。
同样,我们可以推测其他的顾问专家系统——可能是它们中的某一些,或者是较简单的系统——能够用在软件开发的其他部分。
在较早实现的用于软件开发的专家顾问系统中存在着很多困难。
在我们假设的例子中,一个关键的问题是寻找一种简单的方法,能从软件结构的技术说明中,自动或者半自动地产生诊断规则。
另外,更加重要也是更加困难的任务是:
寻觅能够清晰表达,深刻理解来龙去脉,前因后果事情的自我分析专家;
开发有效的技术——抽取专家们所了解的知识,把它们精炼成基础规则。
这项工作的工作量是知识获取工作量的两倍。
构建专家系统的必要前提条件是拥有专家。
专家系统最强有力的贡献是给缺乏经验的开发人员提供服务,用最优秀的开发者的经验和知识积累为他们提供指导,这是非常大的贡献。
最优秀和一般的软件工程实践之间的差距是非常大的,可能比其他工程领域中的差距都要大,一种传播优秀实践的工具特别重要。
“自动”编程。
近四十年,人们一直在预言和编写有关“自动编程”的文字,从问题的一段陈述说明自动产生解决问题的程序。
现在,仍有一些人期望这样的技术能够成为下一个突破点。
[7]
Parnas暗示这是一个用于魔咒的术语,本身的语义是不完整的,并断言:
一句话,自动编程总是成为一种热情,使用现在并不可用的更高级语言编程的热情。
[8]
他指出,大多数情况下所给出的技术说明本质上是问题的解决方法,而不是问题自身。
可以找到一些例外情况。
例如,数据发生器的开发技术就非常实用,并经常地用于排序程序中。
一些微积分方程系统也允许直接描述问题。
系统评估若干参数,从问题解决方案库中进行选择,生成合适的程序。
这样的应用具有非常良好的特性:
●问题通过相对较少的参数迅速地描述特征;
●存在很多已知的解决方案,提供了可供选择的库;
●在给定问题参数的前提下,大量广泛的分析为选择具体的解决技术提供了清晰的规则。
具有上述简洁属性的系统是一个例外,除此之外很难看到该方法能普及到更广泛的寻常软件系统,甚至难以想象这种突破如何能够进行推广。
图形化编程。
在软件工程的博士论文中,一个很受欢迎的主题是图形化或可视化编程,计算机图形在软件设计上的应用。
[9]这种方法的推测部分来自VLSI芯片设计的类比,计算机图形化在该设计中扮演了高生产力的角色。
部分源于——人们将流程图作为一种理想的设计介质,并为构建它们提供了很多功能强大的实用程序——这证实了图形化的可行性。
不过,上述方法中至今还没有出现任何令人信服,更不用说令人激动的进步。
我确信将来也不会出现。
首先,如同我先前所提出的,流程图是一种非常差劲的软件结构表达方法。
[10]实际上,它最好被视为是Burks,vonNeumann和Goldstine试图为他们所设计的计算机提供的一种当时迫切需要的高级控制语言。
如今的流程图已经变得复杂了,一张图有若干页,有很多连接结点,这种表现形式实在令人同情。
流程图已经被证明是完全不必要的设计工具——程序员是在开发之后,而不是之前绘制描述程序的流程图。
其次,现在的屏幕非常小,就像素级别,无法同时表现软件图形的所有正式、详细的范围和细节。
现在所谓“类似桌面”的工作站实际上像是“飞机坐舱座椅”。
在飞机上任何坐在两个肥胖乘客之间,反复挪动一大兜文件的人都会意识到这中间的差别——每次只能看到很少的内容。
真正的桌面提供了很多文件的总览,让大家可以随意地使用它们。
此外,当人们的创造力一阵阵地涌现时,开发人员大多数都会舍弃工作台,使用空间更为广阔的地板。
要使我们面对的工作空间满足软件开发工作的需要,硬件技术必须进一步发展。
更加基本的是,如同我们上面所讨论的,软件非常难以可视化。
即使用图形表达出了流程图、变量范围嵌套情况、变量交叉引用、数据流和层次化数据结构等等,也只是表达了某个方面,就像盲人摸象一样。
如果我们把很多相关的视图叠加在所产生的图形上,那么很难再抽取出全局的总体视图。
对VLSI芯片设计方法的类推是一种误导——芯片设计是对两维对象的层次设计,几何特性反映了它的本质特性,而软件系统不是这样。
程序验证。
现代编程的许多工作是测试和修复bug。
是否有可能出现银弹,能够在系统设计阶段、源代码阶段就消除bug?
是否可以在大量工作被投入实现和测试之前,通过采用证实设计正确性的“深奥”策略,彻底提高软件的生产率和产品的可靠性?
我并不认为这里能找到魔法。
程序验证的确是很先进的概念,它对安全操作系统内核等这类应用是非常重要的。
不过,这项技术并不能保证节约劳动力。
验证要求如此多的工作量,最终却只有少量的程序能够真正得到验证。
程序验证并不意味着零缺陷的程序。
这里也没有什么魔术,数学验证仍然可能是有错误的。
因此,尽管验证可能减少程序测试的工作量,却不能省略程序测试。
更严肃地说,即使完美的程序验证也只能建立满足技术说明的程序。
这时,软件工作中最困难的部分已经接近完成,形成了完整和一致的说明。
开发程序的一些必要工作实际上已经变成了对技术规格说明进行的测试。
环境和工具。
向更好的编程开发环境研究中投入,我们可以期待得到多少回报呢?
人的本能反应是首先着手解决高回报的问题:
层次化文件系统,统一文件格式以获得一致的编程接口和通用工具等。
特定语言的智能化编辑器在现实中还没有得到广泛应用,不过它们最有希望实现的是消除语法错误和简单的语义错误。
开发环境上,现在已经实现的最大成果可能是集成数据库的使用,用来跟踪大量的开发细节,供每个程序员精确地查阅信息,并在整个团队协作开发中保持最新的状态。
显然,这样的工作是非常有价值的,它能带来软件生产率和可靠性方面的一些提高。
但是,由于自身的特性,目前它的回报应该很有限。
工作站。
随