跳到主要内容

设计是选择:从外部行为到内部结构

这一章讲四件事: 为什么需求到设计「翻译」不过去、必须当「选择」来做; 怎么保证选出来的东西一个需求都没漏(一张二维表的走查);怎么让几十人做出来的设计看起来像一个人做的; 以及为什么真正要命的是方向性的概念错误,不是字句层面的语法(写没写对)错误。下一章讲选完之后怎么给「未来的变化」留余地。

1. 先看现象:「设计就是照着需求搭」

流传最广的设计迷信是:需求文档里已经「隐含」了系统长什么样,设计只是把它展开。

作者用三步拆掉这个迷信1: 如果某方法宣称「需求里的架构就是软件架构」,那它只可能在三件事里猜中一件—— 要么需求阶段根本没考虑最优设计(那这张架构图不可当真); 要么需求阶段做了彻底设计(占开发总成本 30%~40%,任何组织都不会在定需求前先烧掉三到四成预算干这个); 要么它假设存在一种对所有软件都最优的架构——这显然不可能。

三个分支全是死路。结论:从「外部行为」到「内部结构」,是一个本质上困难的问题,没有直通车。

先立两个词。架构:系统拆成哪些部件、部件之间怎么连接的总体结构; 算法:解决某个具体问题的一串明确步骤。设计 = 定架构 + 为每个部件定算法, 产出物叫设计规格说明2

2. 顶层全景

需求规格说明(外部行为:「系统对外像什么」)


列出多种候选架构 ── 换设计方法,就换候选(一种方法只会长出一种架构)


按目标逐个权衡:吞吐/响应/可变更/可移植/互操作/安全/可用


选定,并写下(没有写下来的设计不是设计)


追溯矩阵:每个部件追回每条需求(全查一遍)


分层 + 多视角 + 概念一致,让设计「知识可控」

图说:这一章管「怎么选、怎么记、怎么查」;「怎么给未来的改动留余地」
全部留给第 06 章。

3. 核心原理

3.1 设计的动作是「选择」,不是「翻译」

其他工程学科也是这么干的:详细列出多种方法,逐个权衡,采用其一3。 架构的选择本质上是在给下面这些目标排序——吞吐量、响应时间、可变更性、可移植性、 互操作性、安全性、可用性——没有任何一个架构同时最优

所以真正的杠杆在这里:想要多种候选架构,就要用多种设计方法—— 一种设计方法倾向于长出一种特定架构,只用一种方法,等于只有一张候选票3

3.2 主走查:一张二维表,查住两类事故

选完了,怎么知道「所有需求都有部件管、所有部件都有需求养」? 原则 62 给的办法朴素到不像设计工具,却能用出两个效果,值得整表走一遍4:

建表: 行 = 系统里所有部件,列 = 需求规格说明里的每条需求(靠第 04 章 3.2 的唯一编号)。 部件参与满足某条需求,就在交叉格画 1。

拿一个订票系统走(部件与打 1 的位置是为演示编的):

R1 创建订单R2 在线支付R3 取消退款R4 告知出票
预订界面11
订单服务1111
支付网关11
短信网关1

表建完,横竖各查一遍:

  • 竖查(列): 每一列有没有至少一个 1?——有一列整列空白,就意味着那条需求没有任何部件管,漏了
  • 横查(行): 每一行有没有至少一个 1?——有一行整行空白,就意味着这个部件什么需求都不满足,删掉

这张表后续还两次承重:上线后出了故障,维护人员按列反查「这条需求动了哪些部件」, 故障排查范围立刻缩小;某个部件要修,按行查出「修它会碰坏哪些需求」。

作者知道有人嫌这张表难维护,他的回应不留退路:你需要这张表去设计或者维护软件, 没有它,你可能设计不出正确的部件,维护期间要花多得多的时间4

3.3 没有写下来的设计,不是设计

原则 64 记录了作者最常听到的一句话:「我已经完成设计了,剩下的工作就是写文档了。」 他的回应是两个类比:你能想象建筑师说「我设计完了,剩下的就是把房子画出来」? 小说家说「我写完了,剩下的就是把它写下来」?5

设计就是在纸面(或其他媒介)上,对架构和算法做选择、抽象并记录这个动作本身。 嘴上的、脑子里的设计无法被评审、无法被追溯、无法在三个月后还被作者本人看懂。

3.4 几十人做出的东西,要像一个人做的:概念一致

原则 71 定义了高质量设计的标志性特征——概念一致:整个系统只使用有限几种设计「形式」, 且每种形式全系统统一。形式指:模块怎么向上报错、软件怎么向用户报错、数据结构怎么组织、 模块之间怎么通信、文档用什么标准……6

判据很形象:设计完成后,它应该看起来是一个人做的——尽管它其实是很多人的产出6

这条原则的力量在「诱惑管理」:设计过程中总会有人想引入自己偏好的新形式。 作者的裁决线画得很清楚:为了一致性、优雅、简单、性能而偏离既定形式——可以让步; 仅仅为了在设计里留下自己的印记——概念一致比自我满足更重要6

配套的一条是「知识可控」(原则 70):让创建者和维护者能完全理解的设计,靠两个手段—— 分层(越往深读细节越多,每层都能先抽象理解整体)加多视角; 且每个部件在每一层只描述外部视角,不掀开内胆7

3.5 真正要命的是概念错误

原则 72 揭了一个行业心理:大家花大力气抓语法错误(拼错的字、漏掉的括号), 因为抓到这种「愚蠢错误」时人还觉得愉悦;而抓到概念性错误(架构方向错了、算法根本不适用), 人会觉得自己能力不行,于是下意识回避去看8

作者给的药方是每个阶段一套自问清单8:

阶段要问自己
需求这是客户想要的吗?
设计这个架构在压力下能正常工作吗?这个算法真的适用于所有场景吗?
编码这段代码的执行和我设想的一样吗?
测试跑完这条测试,我确信了什么?

顺带补上尺子:为什么概念错误杀伤力大?因为它在 3.1 的「选择」层面错了—— 架构选错,每一个部件的实现都同时在错;语法错,只有一个字符在错。

3.6 设计的多维与合格设计师

设计是多维的(原则 81):像描述一栋房子要立面图、平面图、电气图、管道图一样, 一份完整的软件设计至少要四张图9:

  1. 打包方案:什么是什么的一部分(层次图,顺带体现封装与数据可见性);
  2. 依赖层次:谁需要谁(箭头=依赖);
  3. 调用关系:谁调用谁(箭头=调用/消息);
  4. 进程组织:哪些部件作为进程(同时运行的一段程序副本)并发(同时推进)执行,谁创建谁销毁。

优秀设计出自优秀设计师(原则 82):好设计的六个特征——简洁、简单、优雅、快速、可维护、易实现—— 「源于灵感和洞察力,而不仅是努力工作或按部就班的设计方法」。对最好的设计者要重点支持, 「他们才是未来」10。这条听起来像口号,但它和第 09 章「人不是资源」是同一条线的需求侧论证: 方法可以平均化下限,上限从来是人给的

懂场景是选择的前置(原则 83):压力下的预期行为、输入频率、响应时间极限、 甚至天气对性能的影响——这些信息不在需求文档的字缝里,在应用场景里; 「无论需求文档写得多好」,最优架构选择都主要基于对场景的理解11

3.7 一个容易漏掉的:阶段不同,语言不同

原则 21 提醒:别的工程领域没人用同一套符号贯穿始终——电气工程师画方框图、电路图、 逻辑图、时序图、状态转换表,各有各的用途12。 软件也一样:需求用需求侧最合适的技术,设计用设计侧的,编码用最合适的语言—— 「一种语言通吃所有阶段」的执念,和「简单方法解决复杂问题」的幻想是同一款(第 01 章 3.4)。

4. 作者的判断与证据

说法谁的证据
彻底设计占开发总成本 30%~40%作者(原则 61)1经验数字,书里未注出处
需求里不可能已含最优架构作者(原则 61)1三分支归谬
追溯矩阵(部件×需求的对照表)必须建作者引 Glass,但立场是自己的4论证(两类事故的防法),未给失败案例统计
优秀设计源于灵感作者引 Brooks10观察性论断
概念错误比语法错误更严重作者引 Brooks8心理观察 + 各阶段自问清单

判断(我们的,不是书里的): 追溯矩阵(3.2)在今天最常见的落地形态不是那张人工维护的大表, 而是需求管理工具里的关联字段加代码提交信息里引用需求编号。工具变了,查表的两个动作没变: 竖查防漏需求,横查防养闲部件。凡是不能横竖各查一遍的「需求—实现」对应关系,都只是装饰。 如果错,会错在: 如果团队小到所有需求都在一个人脑子里(两三人小队),矩阵的全部价值确实可以被口口相传替代—— 但这条替代通道在第一次人员变动时就断,而矩阵不会。

5. 边界与局限

  • 「30%~40%」是瀑布世界的预算口径1。今天增量交付的团队不会先花三成成本做全部设计; 这句话 today 的读法是「设计决策的总成本和实现同量级,别想跳过它」,不是「先画三个月图」。
  • 概念一致的代价是约束个人风格。在大团队它救火,在两人小队它可能纯粹碍事—— 形式清单该多严,和团队规模、人员流动率成正比。
  • 「多张图」的清单是 1994 年的。打包/依赖/调用/进程四张图9今天对应模块图、依赖图、 调用链、部署图,工具自动生成——但「每张图回答一个问题」的纪律没变。
  • 追溯矩阵的维护成本真实存在。书里只说「难维护但你必须」4; 现实里它烂掉的最常见方式是需求改了矩阵没改——所以第 11 章的变更控制是它的配套工程。

6. 可带走的

  1. 需求→设计不是翻译是选择:候选架构不止一个,单一方法只能给你一张候选票;
  2. 架构选择=给七个目标(吞吐/响应/可变更/可移植/互操作/安全/可用)排序,没有全优解;
  3. 建组件×需求矩阵,竖查防漏需求、横查防养闲部件——查不出问题的对应表只是装饰;
  4. 「设计完就差写文档」是自欺:写下来的那一刻设计才存在;
  5. 全系统只用有限几种设计形式,成品要「看起来一个人做的」;偏离形式只为一致/优雅/简单/性能,不为留名;
  6. 每个阶段问那一阶段的概念问题——「这段测试让我确信了什么」比「测试过没过」值钱;
  7. 设计至少四张图:打包、依赖、调用、进程——每张图回答一个问题;
  8. 上限靠人:再好的方法也只保下限,最好的设计师是组织级资产。

7. 原文地图

主题原书章原文位置
设计的定义(架构+算法)第4章 设计原则text/16-ch04.txt:7(搜「架构」)
需求→设计转换难(30%~40%)第4章 设计原则text/16-ch04.txt:19(搜「30%到40%」)
评估备选方案(七目标)第4章 设计原则text/16-ch04.txt:41(搜「吞吐量」)
追溯矩阵(空行/空列)第4章 设计原则text/16-ch04.txt:31(搜「二维表格」)
没文档的设计不是设计第4章 设计原则text/16-ch04.txt:49(搜「小说家」)
知识可控(分层+多视角)第4章 设计原则text/16-ch04.txt:117(搜「分层构建」)
概念一致(像一个人做的)第4章 设计原则text/16-ch04.txt:73(搜「一个人」)
概念错误比语法错误严重第4章 设计原则text/16-ch04.txt:131(搜「概念性错误」)
智力距离第4章 设计原则text/16-ch04.txt:95(搜「智力距离」)
设计多维(四张图)第4章 设计原则text/16-ch04.txt:247(搜「立面图」)
优秀设计出自优秀设计师第4章 设计原则text/16-ch04.txt:117(搜「优雅」)
理解应用场景(天气)第4章 设计原则text/16-ch04.txt:271(搜「天气」)
不同阶段不同语言第2章 一般原则text/14-ch02.txt:201(搜「方框图」)

Footnotes

  1. 出处:「第4章 设计原则」第 19 段(text/16-ch04.txt:19,搜「30%到40%」)。三分支归谬:没考虑最优设计 / 负担不起彻底设计 / 假设存在万能架构,同段。 2 3 4

  2. 出处:「第4章 设计原则」第 7 段(text/16-ch04.txt:7,搜「架构」)。设计两活动与产出定义同段。

  3. 出处:「第4章 设计原则」第 41 段(text/16-ch04.txt:41,搜「吞吐量」)。「一些设计方法会导致特定的软件架构。因此,要有多种架构,就要使用多种设计方法」同段。 2

  4. 出处:「第4章 设计原则」第 31 段(text/16-ch04.txt:31,搜「二维表格」)。空行=无用组件、空列=需求未满足、「你需要这张表格」同段;依赖唯一编号(原则 52)同段。 2 3 4

  5. 出处:「第4章 设计原则」第 49 段(text/16-ch04.txt:49,搜「小说家」)。

  6. 出处:「第4章 设计原则」第 121 段(text/16-ch04.txt:121,搜「概念一致」)。形式清单、「看起来都是一个人做的」、诱惑的两分法(可让步/不可让步)同段。 2 3

  7. 出处:「第4章 设计原则」第 117 段(text/16-ch04.txt:117,搜「分层构建」)。「组件应该仅从外部视角描述」同段。

  8. 出处:「第4章 设计原则」第 131 段(text/16-ch04.txt:131,搜「概念性错误」)。心理观察与四阶段自问清单同段。 2 3

  9. 出处:「第4章 设计原则」第 247 段(text/16-ch04.txt:247,搜「立面图」)。房子类比与软件四视角同段。 2

  10. 出处:「第4章 设计原则」第 117 段(text/16-ch04.txt:117,搜「优雅」)。 2

  11. 出处:「第4章 设计原则」第 271 段(text/16-ch04.txt:271,搜「天气」)。

  12. 出处:「第2章 一般原则」第 201 段(text/14-ch02.txt:201,搜「方框图」)。