04-文档组织
文档组织——结构、逻辑、内容槽位与逐项审查一句话
结构管「放什么、放哪里」,逻辑管「为什么、怎么连」,具体管「里面到底写什么」,审查管「分别有没有问题」。
无论是写文档、文章,还是分析复杂问题,都可以避免一上来就陷入细节。
一、结构与逻辑的区分结构 = 总和分、上下层次、组成关系
整体由什么组成
每部分属于哪里
哪些是总体、子系统、模块、组件
例如:系统 → 服务 → 模块 → 类 → 函数
逻辑 = 前因后果、条件关系、推导关系、执行关系
为什么产生什么
什么条件下做什么
做了 A 之后导致 B,B 又作为 C 的前提
例如:输入 → 解析 → 判断 → 分发 → 执行 → 输出
「不是东一榔头西一榔头」,正是逻辑性的核心。
结构回答「东西怎么组织」,逻辑回答「事情怎么发生」。
以服务器为例结构(服务器由哪些东西组成):
1234567Server├── Network├── Protocol├── Router├── Service├── Domain└── Infrastructure
逻辑(请求怎么一步步变成结果):
12客户端请求 → 网络接 ...
03-设计表示
设计表示——多视图模型:八种表示方式与从设计到实现的顺序一句话
「自顶向下设计」本质上不是一种图,而是一套多视图模型:文字定义「是什么」,图表示「怎么关联」,表格管理「先后和状态」,代码实现「具体怎么做」。
自顶向下设计到最终实现,最好不要只用一种东西。真正好用的是把不同层次的信息交给不同的表示方式。
一、八种表示方式
表示方式
最适合表达什么
解决的问题
文字说明
目标、规则、职责、约束
「这个东西到底要干什么?」
层级树 / 思维导图
从系统到模块到功能的分解
「大问题怎么拆成小问题?」
流程图
执行顺序、分支、循环
「运行时先做什么、后做什么?」
依赖图
A 依赖 B,模块之间的关系
「谁依赖谁?」
时序图
多对象之间按时间发生的交互
「Player、Input、Physics、Render 谁先和谁通信?」
状态图
状态变化
「玩家从 Idle 怎么进入 Run / Jump / Dead?」
表格
模块、接口、任务、优先级、状态
「具体要做哪些东西?」
代码 / 接口定义
最终可执行结构
「实际 ...
02-系统设计
系统设计——系统设计师与软件设计师的分工,以及自顶向下的局限一句话
系统设计师关注「整个系统怎么构成和协同工作」,软件设计师关注「软件本身怎么设计和实现」。设计可以自顶向下,实现通常自底向上,验证则逐层向上扩展。
一、系统设计师 vs 软件设计师核心区别1234567系统设计师 ↓系统架构、硬件、软件、网络、部署、安全、性能 ↓软件设计师 ↓软件架构、模块划分、接口、算法、代码实现
设计范围系统设计师(System Designer / System Architect) 设计的是完整系统。例如银行交易系统,系统设计师考虑:
服务器怎么部署?数据库用什么?是否需要缓存?
网络架构如何?如何保证高并发?如何保证安全?如何容灾?
用户端、服务器、第三方系统如何通信?是否需要消息队列?硬件资源如何规划?
软件设计师(Software Designer) 设计的是软件内部结构。例如上面的交易服务,软件设计师考虑:
类怎么设计?模块怎么拆?接口怎么定义?
数据结构怎么选择?算法怎么实现?
如何降低耦合?如何提高可测试性?
方面
系统设计师
软件设计 ...
01-设计起点
设计起点——外部交互边界决定第一设计对象一句话
不同程序的「外部交互边界」不同,因此第一设计对象也不同。先设计程序「怎么被外部使用」,再设计内部「怎么实现」。
拿到一个新项目,第一步容易迷失在「先写哪个类、哪个文件、哪个函数」里。设计起点告诉你:先回答「这个程序怎么被外部使用」,再逐层向内设计。
一、外部交互边界决定第一设计对象
程序类型
最先设计的核心东西
它实际上定义了什么
CLI
入口参数 / 命令
用户如何调用程序
通信程序
通信协议 / 消息
两端如何约定通信
GUI
UI / 用户操作流程
用户如何操作程序
TUI
UI / 用户操作流程
用户如何操作程序
库
API / 接口
其他程序如何调用它
服务器
协议 + 请求模型
外部如何请求服务
驱动
驱动接口 / I/O 模型
OS 或上层如何使用驱动
每一行都指向同一个动作:程序与外部世界「打交道」的那个边界。
边界在命令行 → 第一设计对象是命令与参数(CLI)
边界在两端通信 → 第一设计对象是协议与消息( ...
03-功能函数与模块
功能函数与模块:组合层的单一职责
单一职责在接口层体现为原子化接口(只做封装+异常处理);在组合层体现为功能函数和模块——功能函数 = 数据结构 + 算法 + 接口,三个部分各司其职;模块 = 一个功能的完整组合。这一篇讲组合层的单一职责:数据、算法、接口怎么分开,怎么组合成一个完整的功能。
一、组合层的单一职责接口层的单一职责是「原子化接口只做封装库接口 + 异常处理」:
1234原子化接口 = 封装库接口 + 异常处理 ✗ 不定义数据结构 ✗ 不实现算法 ✗ 不做业务逻辑
组合层的单一职责是「功能函数 = 数据结构 + 算法 + 接口」——三个部分各司其职:
1234功能函数├── 数据结构 ← 数据怎么表示(一个职责)├── 算法 ← 怎么计算(一个职责)└── 接口 ← 怎么被调用(一个职责)
每个部分职责单一,组合起来是一个完整功能。
二、功能函数 = 数据结构 + 算法 + 接口1234567891011121314// 数据结构:一个职责——表示内存块typedef struct & ...
02-原子化接口
原子化接口的封装与异常处理
功能函数 = 数据结构 + 算法 + 库提供的接口,这是组件的基本形态。当模块被更多地方调用时,库提供的原始接口需要被封装成原子化接口。原子化接口本身只做两件事:封装库提供的接口和异常处理。它不包含数据结构,也不包含算法——它是独立的封装层。
一、为什么需要原子化接口直接调用库提供的原始接口有两个问题:
12345678910111213141516// 问题 1:没有异常处理int fd = open(path, O_RDONLY);// 如果 open 失败返回 -1,代码继续执行// 后续的 mmap(fd, ...) 会用 -1 作为 fd → 崩溃// 问题 2:一个功能散落在多处void do_task() { int fd = open(path, O_RDONLY); // 到处都是 open // ... close(fd);}void do_other() { int fd = open(path, O_RDONLY); // 又是 open // ... ...
01-单一职责原则
单一职责原则:一件事只做一件事
单一职责是软件组织的第一原则:一个东西只做一件事。 它不是某个粒度的规则,而是贯穿所有粒度的主题——函数、类、文件、模块、组件、系统,每一层都在执行同一个原则。职责混乱就拆分,拆到「每个单元只承担一个职责」为止。
一、原则一句话
一个东西只做一件事。
更准确地说:
职责单一,按变化原因拆分,而不是单纯因为数量多就拆。
12❌ 一个函数 100 行但只做一件事 → 不该拆(职责单一)✅ 一个函数 20 行但做三件事 → 该拆(职责不单一)
判断标准是职责,不是大小。
二、单一职责贯穿所有粒度12345678函数 只做一个操作(一个明确操作)类 只封装一个对象(对象/状态/行为的封装)文件 只承担一个职责(编译单元)模块 只负责一个功能(独立功能单元)组件 只提供一个功能单元(数据+算法+接口)子系统 只负责一类相关功能(一组相关功能)分层 每层只处理一类问题(界面/业务/基础设施)原子化接口 只做封装库接口 + 异常处理
123MovementSystem 只负责移动 ...
09-认证与请求上下文
认证与请求上下文——Middleware / Token / Session 的位置
请求进入服务器后,不是直接进业务——在 Router 之前通常还有一层横向处理:认证、权限、日志、限流。这一条线回答三个问题:① Router / Controller / Middleware 各自的职责是什么、认证放在哪(不是 Controller 的职责);② Connection State(网络层)/ Session(应用层会话)/ Token(凭证)三者怎么区分;③ Controller 不是强制层——职责重复就拆分,没有职责就不要增加层。
一、请求进入服务器后的实际流动顺序不要把 Router、Controller、认证、Session 当成平级的「层」:
123456789101112131415161718192021222324252627客户端 │ │ HTTP Request ▼┌─────────────────────┐│ HTTP Server │ ← 通信协议└─────────┬─ ...
08-GUI入口的进一步拆分
GUI 的 MVVM:入口的进一步拆分
CLI 的入口骨架是「接受 → 解析 → 分发 → 调用」。GUI 把这个骨架进一步拆分了:传入发生在控件交互里,分发发生在不同的用户流程里,调用发生在 ViewModel 层;而 View 与 ViewModel 之间不再靠函数调用传递,而是靠状态变化同步——状态一变,界面自己刷新。验证也因此分成两种:交互验证、状态变化的界面验证。
一、入口骨架回顾从指令到系统的主线里,入口长这样:
1接受 → 解析 → 分发 → 调用 → 业务
CLI 里这四个动作都在 main 里串行完成:
1234main ├── parse(argc, argv) ← 接受 + 解析 ├── dispatch(cmd, handlers) ← 分发 └── handler(&data) ← 调用
GUI 也是入口,但它不能沿用这个骨架——因为 GUI 不是「一次执行然后退出」,而是长期运行、事件驱动。用户随时可能点任何按钮。所以 GUI 的入口必须重新拆分。
二、GUI 的入口拆成了页面与调用GUI 把入口骨 ...
07-服务器入口的解构
服务器入口的解构:接受(接口)→ 解析 → 分发 → 调用
主线 01 把 CLI 入口解构成「接受 → 解析 → 分发 → 调用」四段骨架。服务器入口同样可以被解构——除了「接受」这一步不同(CLI 接收参数,服务器通过网络接口接收消息),后面的解析层、分发层、调用层三层结构与 CLI 完全一样。这一篇是「服务器入口的解构」独立线:说明服务器入口为什么也是四段骨架、每一段在服务器里的具体形态是什么、以及它和 CLI 入口的唯一差异。
一、入口骨架是同一个:接受 → 解析 → 分发 → 调用无论 CLI 还是服务器,「入口」做的事都是同一件事:把外部输入变成对业务的调用。
12入口骨架(主线 01 解构出来的四段): 接受 → 解析 → 分发 → 调用 → 业务
CLI 和服务器都是这个骨架的具体实例,只是每段的「输入长什么样」不同:
12CLI 入口: 接受(参数 argc/argv)→ 解析 → 分发 → 调用 → 业务服务器入口: 接受(网络接口消息) → 解析 → 分发 → 调用 → 业务
骨架相同,接受不同。
二、服务器入口的四个阶段以一次 HTTP 请求为 ...
