系统设计的两种抽象:隐藏与简化
摘要
文章区分了系统设计中的两种抽象类型:模块化抽象,它隐藏内部细节;建模抽象,它将系统简化为基本行为以进行形式化推理。
暂无内容
查看缓存全文
缓存时间: 2026/09/04 15:06
# 系统设计的双重抽象:隐藏与精简
来源:http://muratbuffalo.blogspot.com/2026/05/the-two-abstractions-of-system-design.html
谈及TLA+时,我常将“抽象”称为最需要掌握的核心要素(https://muratbuffalo.blogspot.com/2023/09/beyond-code-tla-and-art-of-abstraction.html),而这恰恰也是最难习得的技能(https://muratbuffalo.blogspot.com/2026/03/tla-mental-models.html)。
但一个矛盾始终困扰着我:计算机科学家本应擅长抽象,抽象本应是操作系统、网络、软件工程的根基,抽象数据类型(ADTs)更是计算机课程的标配。为何我(以及其他所有从事形式化方法/建模研究的人)仍观察到如此显著的抽象能力鸿沟,并将此视为建模成败的关键?
我终于看清了这个认知失调的根源:存在两种被混为一谈的“抽象”。
- **模块化抽象**:即传统计算机课程教授的ADT、API、分层设计等抽象形式。其核心是封装、划定边界和隐藏内部细节。
- **建模抽象**:指我在建模语境中讨论的抽象,与数学家和物理学家构建思维模型时的抽象同义。其目标是找到保留核心特性的最小化、最精炼的描述,即剔除所有与本质属性无关的冗余部分。
两者的目标截然不同!请容我在接下来两节展开阐述。
## 模块化抽象用于隐藏,建模抽象用于精简
模块化抽象关注隐藏内部细节的接口;建模抽象关注行为本身,旨在将系统精简至与目标属性相关的最小行为骨架。
模块化抽象进行封装,划定纵向边界以隐藏底层;建模抽象则具有横切性——它沿行为平面对系统进行剖析,只保留与研究属性绝对相关的部分,且仅呈现“做什么”而非“怎么做”。这种剖析结果往往与系统原有结构大相径庭。
## 模块化抽象隐藏并发性,建模抽象暴露并发性
模块化抽象致力于封闭所有泄漏(正如乔尔·斯波尔斯基那篇著名的博文《抽象法则皆有漏洞》所指出的:https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/)。请注意,他所列举的正是模块化抽象案例:TCP(隐藏IP层)、字符串库(隐藏字符数组)、文件系统(隐藏旋转磁盘)、虚拟内存/平面地址空间(隐藏MMU与分页)、SQL(隐藏查询计划)、NFS/SMB(隐藏网络)、C++字符串类(隐藏char*)。模块化抽象试图隐藏线程交错现象,使操作看起来如同原子操作般完整。其目标在于简化模块使用,却也因之放弃了暴露并发性或效率优化的机会。
与之形成鲜明对比的是,建模抽象旨在识别并利用那些应当“泄漏”的特性!它暴露细粒度的操作与顺序,并证明即使存在交错执行,不变式依然成立。这种努力带来的回报是从系统中榨取最大限度的安全并发能力。
## 建模抽象实例
分布式系统领域充斥着丰富的建模抽象案例,几乎大多数协议都遵循此设计思路:
- **Lamport逻辑时钟**:舍弃物理时间,保留先后发生关系
- **混合逻辑时钟**:同时保留物理时间与因果关系,舍弃其余信息
- **TrueTime**:将时间建模为有界不确定性区间
- **共识算法**:就单一决策达成一致。Lamport设计Paxos的过程堪称抽象艺术的典范:从共识、投票到最终协议(https://lamport.azurewebsites.net/tla/paxos-algorithm.html)
- **线性一致性(及所有一致性模型)**:舍弃复制、缓存、重试机制
- **日志即数据库**:摒弃以物化状态作为数据源,仅保留有序、仅追加的事件序列
- **MapReduce/Spark**:舍弃编排、并行、调度与容错机制,仅保留确定性转换在分区数据上的有向无环图——其余一切由框架基于此骨架重构
看似存在定义重叠(如线性一致性、共识、日志即数据库、MapReduce),但这实属概念复用而非交叉。设计精良的**制品**可同时充当**需据此完善的技术规格**(模块化抽象)和**推理用的基础骨架**(建模抽象)。这仅意味着两种抽象在同一个制品上达成了统一,但其抽象角色依然泾渭分明。
相似文章
@_streetdogg: 计算机系统中的抽象层次 计算机系统通过多层抽象构建,使得复杂操作…
本文解释了计算机系统中的抽象层次,详细说明了软件如何通过多个层级与硬件交互,直至硅芯片层面。
宁可重复,也不要错误的抽象(2016)
文章认为,重复比错误的抽象代价更低,并建议开发者避免强行引入抽象,以免后续因条件判断而变得复杂。
控制与复杂性:系统设计中的张力
本文探讨了系统设计中控制与复杂性的张力,特别是在软件开发中采用LLM的背景下,对比了分析分解与复杂系统方法。
简洁不是微小
文章主张,软件设计中的简洁性不同于规模的小巧,并通过Unix和Clojure的例子说明,简单的系统在修改后也可能变得复杂。
两种设计方式
讨论软件设计的两种方法,如C2 wiki中所记载。