01-设计起点
设计起点——外部交互边界决定第一设计对象
一句话
不同程序的「外部交互边界」不同,因此第一设计对象也不同。先设计程序「怎么被外部使用」,再设计内部「怎么实现」。
拿到一个新项目,第一步容易迷失在「先写哪个类、哪个文件、哪个函数」里。设计起点告诉你:先回答「这个程序怎么被外部使用」,再逐层向内设计。
一、外部交互边界决定第一设计对象
| 程序类型 | 最先设计的核心东西 | 它实际上定义了什么 |
|---|---|---|
| CLI | 入口参数 / 命令 | 用户如何调用程序 |
| 通信程序 | 通信协议 / 消息 | 两端如何约定通信 |
| GUI | UI / 用户操作流程 | 用户如何操作程序 |
| TUI | UI / 用户操作流程 | 用户如何操作程序 |
| 库 | API / 接口 | 其他程序如何调用它 |
| 服务器 | 协议 + 请求模型 | 外部如何请求服务 |
| 驱动 | 驱动接口 / I/O 模型 | OS 或上层如何使用驱动 |
每一行都指向同一个动作:程序与外部世界「打交道」的那个边界。
- 边界在命令行 → 第一设计对象是命令与参数(CLI)
- 边界在两端通信 → 第一设计对象是协议与消息(通信程序)
- 边界在用户操作 → 第一设计对象是 UI(GUI / TUI)
- 边界在程序间调用 → 第一设计对象是 API(库 / 服务器 / 驱动)
所以不能笼统说「所有软件都是先设计 UI / 接口」,而要看外部交互边界具体落在哪里。
二、四类程序的展开
1. CLI 先设计入口参数
做一个 image-tool:
1 | image-tool input.png --resize 800x600 --output out.png |
首先确定的是:命令 → 参数 → 参数含义 → 输出:
1 | image-tool |
然后才往里设计:参数解析 → 业务调用 → 算法 → 文件输出。
CLI 的外部契约就是命令行接口。 外部世界(用户 / Agent / 其他脚本)通过命令与参数控制系统,所以第一设计对象是入口参数,而不是内部类、内部函数。
2. 通信程序先设计协议
客户端和服务器做登录,先确定消息格式:
1 | Client → Server |
然后才设计:协议 → 解析 → 分发 → Handler → Service → Domain。
两个独立运行的系统如何达成共同理解? 两端各自独立运行、互不共享内存,唯一能对齐的就是协议。协议先行,解析、分发、业务处理都建立在协议之上。
3. GUI 先设计 UI
GUI 的入口是用户操作。首先需要知道界面的组成:
1 | 窗口 |
以及用户操作发生什么:
1 | 用户点击按钮 → 发生什么 → 数据怎么变化 → 界面怎么更新 |
然后再设计 View / ViewModel / Service / Domain / Infrastructure。
「可以先只画窗口和 UI 在 View 层,然后数据分离吗?」其实就是这个思想:UI 定义了程序与用户的交互边界,边界确定后再决定数据如何变化、内部如何分层。
4. TUI 本质上也是先设计 UI
有状态 TUI 更接近 GUI,而不是 CLI。例如进程列表界面:
1 | ┌──────────────────────────────┐ |
首先要确定:界面有哪些区域、有哪些状态、用户有哪些操作、焦点怎么移动、页面怎么切换。然后才是 TUI View → 状态 → Controller/ViewModel → Service → Domain。
无状态 CLI → 先设计命令接口。有状态 TUI → 先设计交互界面。
这也是为什么「CLI 很适合 Agent,但调试器必须有调试会话和状态」——调试器是典型的有状态程序,会话与状态是它的核心,不能退化成纯命令式 CLI。
三、统一规律:先设计系统对外的控制面
把上面全部收拢成一句:
先设计「系统对外的控制面」。
不同程序的控制面不同:
1 | CLI → Command / Argument |
然后才往里面倒推:
1 | 外部接口 |
这个倒推结构和一个更早的结论接得上:接口决定「怎么控制系统」,架构决定「系统内部怎么实现这个控制」。
四、为什么 CLI + 静态库特别容易闭环
这也解释了为什么 CLI + 静态库 特别容易形成闭环:
- CLI 提供控制入口(控制面)
- 静态库提供稳定能力(实现面)
- 两者之间的边界非常清楚
边界清楚,双向演化(CLI 反馈需求 → 静态库补充能力)就不容易失控。
五、实用原则
一句原则:
先设计程序「怎么被外部使用」,再设计内部「怎么实现」。
向内拆解链条:
1 | 入口 → 控制流程 → 业务 → 数据 → 算法 → 基础设施 → 具体实现 |
用途:
这样拿到一个新项目,第一步就不容易迷失在「先写哪个类、哪个文件、哪个函数」里——先回答「这个程序怎么被外部使用」,再逐层向内设计。
与其他线的关系
- 与系统角色主题:系统角色决定程序由哪些角色组成;设计起点决定这些角色中先设计哪一个的对外控制面
- 与从输入到交互线:从输入到交互讲用户流程的形态(运行时);设计起点讲该形态对应的程序先设计什么(设计时)
- 与服务器线:服务器的控制面是「协议 + 请求模型」,与设计起点的「通信程序先设计协议」一致
