给编辑的信

1992年5月19日
来自:Jack W. Reeves,加利福尼亚州圣何塞
致:Livleen Singh,编辑,C++期刊,华盛顿港,纽约

 

亲爱的编辑,


  感谢您发表我1991年8月27日对软件设计发表评论的信。我同意(原则上,如果不是详细)您的大部分回复。我们都希望找到生产更好软件的方法并帮助我们的行业“成熟为一门学科。”我的问题是,大约十年前我得出的结论是,作为一个行业,我们不了解软件设计的真正含义。今天我更加坚信这一点。我不主张我的观点是正确的,但我发现它在解释为什么有些事情有效而为什么其他事情无效方面非常有用。有一个非常微妙但非常重要的一点被忽略了。这就是做软件设计之间的区别以及真正完成的软件设计是什么。我想详细说明这一点。


  请允许我从您回复的最后一部分开始。我声明(提供上下文)“这可能不是一个很好的(软件)设计,但它是一个(软件)设计。”您建议将软件设计与桥梁设计进行比较,并创建了以下陈述“它可能不是一个很好的桥梁,但它确实是一座桥梁。”这里用“桥梁”一词代替“(软件)设计”。这种解释似乎是“你会相信被建造而几乎没有没有设计的东西吗?”显而易见的答案是“当然不是!”然而,这种比较是无效的。对于大多数工业来说看起来有效的这一事实正是我想要表达的重点。


  不是改变句子,而是改变上下文。现在的陈述是“这可能不是一个很好的(桥梁)设计,但它是一个(桥梁)设计。”你会自愿成为第一个穿过这座桥的人吗?直接的答案可能是“不,糟糕的设计并不比没有设计好!”稍微思考一下就会表明同样有效的答案是“什么桥?”在您真正从设计中建造桥梁之前,无需担心穿过它。关键是我们不会从仓促的设计中建造桥梁。在桥梁实际建造之前,设计将大大改进。我们会做分析。我们将建立计算机模型并运行模拟。我们甚至可以建立比例模型并在风洞和其他方式中对其进行测试。在我们建造桥梁之前,我们将尽一切努力确保设计是一个好的设计。我们称这个为“工程”(有时,尽管如此,它仍然不完全正确……塔科马有这座桥)。


  回到软件。我们在软件行业也改进我们的设计,只是我们不称之为工程。我们称之为“测试和调试”。软件生命周期的这个阶段需要很长时间。经常需要比计划的时间要长。不幸的是,通常这并不充分,我们变成可交付软件的最终设计仍然没有它们应有的那么好。这似乎是软件生命周期中的一个事实。许多人对此感到遗憾并问为什么我们软件开发人员没有更好地“工程化”我们的设计?有很多的解释,但对我来说从来都没有一个是最明显的——简单的经济学。软件的构建成本非常低。


  我疯了吗?我不这么认为!在您的486上编译和链接50,000行C++代码似乎要花很长时间,但是您希望如何组装具有50,000个独立组件的电路卡,或构建具有50,000个结构元素的桥梁?我们不构建软件正确性的数学证明或通过符号执行器运行我们的代码,因为构建和测试它需要更少的时间和精力。如果我们做更多的前者,我们可能会得到更好的软件,但我们没有。为什么不?


  可能有很多原因,但我想建议其中许多原因是我们没有将测试和调试视为软件设计过程的一部分。我们希望它完全消失。既然它不会,我们尝试将其视为某种“质量保证”功能,并尽可能少地花费时间、精力和金钱。测试和调试要占据典型的软件开发周期的一半的时间,这让我们视为软件工业的羞耻。真的这么糟糕吗?我怀疑其他学科的大多数工程师都不知道实际创建设计所花费的时间百分比以及用于测试和调试结果的时间。一些行业可能是比软件更好。我很确定其他行业实际上要差得多。考虑“设计”一架新客机必须采取什么措施。


  当人们开始在软件设计和其他工程学科之间进行无端的比较时,我变得有些暴躁。主要的微处理器逻辑上存在错误,桥梁倒塌,水坝破裂,飞机从天而降,成千上万的汽车和其他消费者召回的产品----所有在最近的记忆和所有设计错误的结果。


  软件的问题是——设计不仅仅是重要的,它基本上是一切。说程序员不应该设计就像说鱼不应该游泳。当我编码时,我在设计。我在凭空创造一个软件设计。有时算法很简单,设计很琐碎。很多时候,我必须设计数据结构和算法来匹配。可能有替代方案,我必须根据我对每种的优点和缺点的看法来在它们之间做出选择。有时我认为设计变得太复杂了,我指定一个或多个子模块。当我完成设计时,我测试它看看它有多好,然后改进它以使其更好。改进不仅会来自发现原始设计中的错误,也来自其他来源,例如伙伴浏览和正式审查。底线是我的设计必须是正确的,否则从它构建的每个软件都是错误的。因此我专注于正确地做,就像任何其他创意设计活动一样,它需要智力上的努力和技能。


  尽管如此,大多数软件系统都相当大且相当复杂,我的一个模块只是一小部分。虽然我专注于模块X的代码设计细节,但可能还有数百个其他模块和数千个其他细节我可以不太可能同时担心。软件设计的一些重要方面也不能完全归入数据结构和算法的范畴。大多数人在说软件设计时指的就是这些“其他方面”。


  确实,程序员在设计代码时不想担心设计的“高级方面”。无论如何,他们往往最终不得不担心。高级设计显然会影响详细设计。反之亦然。也是如此。内部设计的细节可能(或应该)帮助在高层替代方案中做出决定。完善设计的所有方面是一个应该在整个开发周期中发生的过程。如果设计的某些方面被“冻结”了在细化过程中,您可能最终会得到一个糟糕的甚至不可行的最终设计。


  我的一些同事将我在这个主题上的长篇大论解释为“杰克说忘记设计并开始编码”。事实并非如此(尽管我明白他们是如何获得这种印象的)。我并不反对传统的软件设计。我们在各个层面都迫切需要好的设计。不管我们把早期的流程称为顶层设计、结构设计、模块设计还是其他什么都没有关系。我一直在争论的是观念上的两个变化。首先,我们认识到早期设计步骤的结果不是一个完整的软件设计,就像第一个粗略的草图是一个完整的桥梁设计。第二,我们使用一个真正的骨架软件设计的符号来捕捉我们的设计思维。这意味着使用编程语言。


  根本上,计算机并不关心我们如何获得最终的软件设计,就像建造桥梁的高架千斤顶关心桥梁设计如何改进和验证一样。对他们中的任何一个来说,重要的是他们正在工作的设计足以让他们正确地构建产品。另一方面,对于我们这些负责创建软件设计的人来说,创建一个好的软件设计所需要的东西显然很重要。早期设计越好,后期需要改进的工作就越少。这才是我们真正要谈论的,不是吗。

 

  我所争论的不是更少的软件设计,而是一个现实的软件设计过程。我们需要认识到设计软件和软件设计之间的区别。使用我们在设计过程中找到帮助我们的任何工具和技术是有意义的。忘记什么是真正的软件设计是没有意义的。当我们解决了软件设计的某些方面时,我们不应该让与硬件工程学科的不正确比较使我们无法正确文档化我们在软件设计中的工作。是的,我们应该对它进行编码。如果我们真正在做的是软件设计,那么我们所做的一切都会以某种方式反映在代码中。我们在做出影响代码的决定时也会写代码(或其中有意义的部分)。

 

  我知道“语言无关”软件设计符号的所有论点。他们都忽略了一个基本问题。软件设计涉及将一些问题空间的概念翻译成编程语言。这种翻译必须由人工完成,由于我们的编程语言通常完全不足以直接表达问题空间的概念,这通常是一个困难且容易出错的过程。当我们把概念从一种形式翻译成另一种形式,尤其是哪些复杂的,我们经常会丢失重要信息。如果涉及多次翻译,我们最终得到的最终产品可能会丢失太多重要信息,不能准确反映我们的原始概念,和/或仅包含错误。每一步的翻译都是不同的。记住,C++(或Ada、C、Smalltalk、LISP或任何编程语言)没有什么神圣的。它不是我们计算机的母语。从根本上说,编程语言本身只是一种设计符号。如果可以避免,我认为引入额外的翻译步骤没有任何意义。

 

  我承认我的“代码即设计”方法存在一些问题。首先,即使是最好的编程语言作为表达软件设计某些方面的工具也存在严重的弱点。信息在代码中(如果不是,那么它不是软件设计信息)但很难以人类可读的形式将其取出。这些是上面提到的软件设计的“其他方面”。第二个问题是类似的。信息从问题空间进入软件设计,但不能从软件设计本身被重新构造。我们希望捕获这些信息,以防我们以后需要更改软件设计。典型的源代码注释不是一种充分的机制。

 

  这两个问题都意味着辅助文档对于软件设计与任何其他工程学科一样重要,如果不是更重要的话。然而,我们必须认识到辅助文档,而不是将其与软件设计混淆。我们真正需要的是更具表现力的编程语言。 这就是我关于C++是软件设计艺术的重大进步的声明的原因。C++是一种更具表现力的编程语言,这使其成为更好的软件设计工具。

 

  作为最后一个主题,请考虑我的观点揭示了传统软件开发过程的哪些方面。最终,所有软件设计过程最终都会通过构建/测试周期来验证和完善最终设计。否则,任何伪装都是愚蠢的。然而,传统的MIL-STD 和其他瀑布模型开发过程甚至不允许编写一行代码,直到产生和审查了一定数量的辅助文档。通常,制作此文档的人会继续做其他事情,留下一群新的、通常更年轻、经验不足的人实际生成真正的软件设计。这个过程已经声名狼藉,以至于没有真正的开发人员提倡它,这并不奇怪(无论如何对我来说)。他们在尝试什么?

 

  现在我们有了快速原型和螺旋式开发。在我看来,很容易理解为什么这些正在取代瀑布方法。这两者都只是在开发周期早期编写代码的借口,以便通过构建改进设计的过程/测试可以尽早开始。他们通常也会让相同的人参与顶层设计和实际代码设计。毫不奇怪,这两种方法都被视为重大改进。即使是最好的传统方法也继续尝试打破软件设计进入具有单独符号和产品的不相交步骤,然后他们继续想知道为什么他们在获得正确的最终软件产品时遇到问题。

 

  在Ada中完成的项目在集成、测试和调试所需的时间方面显示出显着改善(以牺牲一些额外的顶级设计工作为代价)。结构化编程在当时被认为是一个突破。面向对象的设计和C++正在走向世界风暴。忘记对这些现象提供的所有解释,并考虑哪些不起作用。案例工具?流行,是;通用,否。结构图?同样的事情。同样,Warner-Orr图,Booch图,对象图,你说出它的名字。每个都有它的优点,还有一个基本的弱点——它真的不是软件设计。最终,编程技术的改进对软件开发来说比其他任何东西都重要。

 

  软件社区似乎有一个集体幻想,如果我们能找到正确的图形设计符号,使软件设计看起来像其他工程设计,那么我们就可以取代其他学科的软件工程师。我不同意。工程是关于你如何做这个过程,而不是关于最终产品是否需要一个CAD系统来渲染它。我们在软件业务中是如此接近,但又如此遥远。软件是如此——嗯,软。一个软件系统可以代表任何东西。这,加上构建/测试周期的经济性,使得我们不太可能找到除当前试错之外的通用方法来验证软件设计。但是,我们可以改进过程。也许如果我们开始将软件开发视为同质设计过程,并专注于改进最重要的阶段(编程、调试和测试),我们可能会发现我们的行业比我们想象的更像是一门严格的科学。

 

  我仍然不知道我是否表达了我的观点,但总而言之:

  • 真正的软件是在计算机上运行的。这意味着真正的软件不是C++(或任何其他编程语言)。
  • 真正的软件是由计算机构建的(通过编译器和链接器)。
  • 这意味着源代码列表(以任何编程语言)实际上是一种软件设计。
  • 从上面可以看出,真正的软件构建起来非常便宜,而且随着计算机的速度越来越快,它一直在变得越来越便宜。
  • 软件的设计成本非常高。这在绝对意义上(因为其不断增加的复杂性)和相对于软件构建的成本都是正确的。
  • 编程是一种设计活动——一个好的软件设计过程会认识到这一点,并且在编码有意义时会毫不犹豫地编码。
  • 编码实际上比传统的软件设计过程更有意义。
  • 测试和调试是设计活动——它们相当于其他工程学科的分析、模拟、建模和测试阶段的软件。目标是在构建最终产品之前验证和改进设计。一个好的软件设计过程会认识到这一点,并且不会试图否认或缩短步骤。
  • 还有其他设计活动——称它们为顶级设计、模块设计或其他。一个好的软件设计过程会认识到这一点,并有意包括这些步骤。
  • 所有的设计活动都是相互作用的。一个好的软件设计过程会认识到这一点,并允许设计改变,有时甚至是彻底改变,因为其他设计步骤揭示了需求。
  • 许多不同的软件设计符号可能是有用的——作为辅助文档(最好有一些工具可以帮助我们从实际源代码中自动生成和维护辅助文档。这对于维护结构方面的图形表示特别有用设计)。
  • 最终,软件开发的真正进步取决于编程的进步。C++是朝着正确方向迈出的良好一步。为了软件工程的缘故,我们需要更多的步骤。

 

历史笔记

  在讨论这封信的出版及其与原始信件关系的电子邮件通信中,

  “事件的顺序如下:我在C++期刊的一期中阅读了一篇关于软件设计的文章。我给编辑发了一封信,抱怨某事(我不记得是什么,而且这些天在我的系统上找不到这封信)。编辑打印了这封信和回复。我所附的是我通过电子邮件发送给编辑的信,作为对他的回应的回应。我想你会觉得它很熟悉。我个人认为它比文章本身写得更好。编辑Livleen Singh提出让我把我的观点变成一篇完整的文章,我做到了。”

posted @ 2021-09-11 18:22  leehsiang  阅读(139)  评论(0编辑  收藏  举报