简介:识别出代码中的惯用模式后,下一步是积累和使用它们。理解设计与代码之间的关系有利于发 现可重用的代码。本期的 演化架构与紧急设计 探索代码与设计的关系,使用表达性强的语言的重要性, 以及重新考虑抽象风格的潜在价值。
通过本 系列 的前几期,您已经知道,我的观点是软件的每个部分都包括可重用的代码块。例如,公 司处理安全性的方式在整个应用程序甚至多个应用程序中可能都是一致的。这就是我所说的 惯用模式 的 实例。这些模式代表对构建软件特定部分时遇到的问题的常用解决方案。惯用模式有两种类型:
技术模式 —— 包括事务、安全性和其他基础结构元素。
域模式 —— 包括单个应用程序内或跨多个应用程序的业务问题的解决方案。
在前几期中,我将大部分注意力放在如何发现这些模式上面。但是,发现模式之后,必须能够将它们 作为可重用代码加以利用。在本文中,我将研究设计与代码之间的关系,特别是表达性强的代码如何使模 式的累积变得更容易。您将看到,有时候通过改变抽象风格,可以解决一些看似难以解决的设计问题,并 且可以简化代码。
设计即代码
早在 1992 年,Jack Reeves 写了一篇题为 “What is Software Design?” 的思维敏锐的论文。在 此文中,他将传统的工程(例如硬件工程和结构工程)与软件 “工程” 作了比较,目的是为软件开发人 员拿掉工程这个词上的引号。这篇论文得出一些有趣的结论。
Reeves 首先观察到,一项工程最终交付的成果是 “某种类型的文档”。设计桥梁的结构工程师不会 交付真正的桥。其最终成果是一座桥的设计。然后,这份设计被传到一个建筑团队手上,由他们来建造真 正的桥梁。对于软件而言,类似的设计文档是什么呢?是餐巾纸上的涂鸦、白板上的草图、UML 图、时序 图还是其他类似的工件?这些都是设计的一部分,它们合起来仍不足以让制造团队做出实际的东西来。在 软件中,制造团队是编译器和部署机制,这意味着完整的设计是源代码 — 完整的 源代码。其他工件只 能为创建代码提供帮助,但是最终的设计成果还是源代码本身,这意味着软件中的设计不能脱离源代码。
Reeves 接下来的观点是关于制造成本的,制造成本通常不算工程的一部分,但是是工件的总体成本估 计的一部分。构建物理实体较为昂贵,这通常是整个生产流程中最昂贵的部分。相反,正如 Reeves 所说 的:
“...软件构建起来很便宜。它廉价得简直就像是免费。”
记住,说这句 话的时候,他正在经历 C++ 编译和链接阶段,这可是非常消耗时间的。现在,在 Java 领域,每 时每刻都有团队冒出来实现您的设计!软件构建现在是如此的廉价,以至于几乎可以忽略。相对于传统的 工程师,我们有着巨大的优势。传统工程师肯定也很希望能够免费地建造他们的设计,并进行假设分析的 游戏。您能想象吗?如果桥梁工程师能够实时地试验他们的设计,而且还是免费,那么造出来的桥梁将会 是多么的精致。
制造是如此容易,这就解释了为什么在软件开发中没有那么高的数学严密性。为 了取得可预测性,传统工程师开发了一些数学模型和其他尖端技术。而软件开发人员不需要那种级别的严 密分析。构建设计并对其进行测试,比为其行为构建形式化的证明要来得容易。测试就是软件开发的工程 严谨度(engineering rigor)。这也导致了 Reeves 的论文中的一个最有趣的结论:
如果软件 设计相当容易被证实,并且基本上可以免费构建,那么毫不奇怪,软件设计必将变得极其庞大而复杂。
实际上,我认为软件设计是人类有史以来尝试过的最复杂的事情,尤其是在我们所构建的软件的 复杂性不断攀升的背景下。考虑到软件开发成为主流也才大约 50 年的光景,通常的企业软件的复杂性已 经令人瞠目。