07-CLI程序的设计顺序
CLI 程序的设计顺序——从不成形想法到静态库的双向闭环
设计起点线说「CLI 先设计入口参数」。这一篇把这句话展开成完整的 CLI 程序设计顺序:从用户不成形想法开始 → 分析 → 程序类型判断 → CLI 需求 → 用户操作 → CLI 能力需求 → API 能力匹配 → 发现缺口回跳静态库流程 → 设计 → 编码 → 审查 → 编译 → 测试。CLI 不是静态库 API 的包装器,而是需求的提出者——它通过与静态库之间的「API 缺口反馈」形成双向演化闭环。
一、CLI 是什么:不是包装器,而是消费者
1.1 错误的流程
1 | 静态库API |
这种「API 列表 → 生成 CLI 代码」的流程容易退化成「为了调用 API 而写的适配程序」:
1 | 没有查询功能? → AI自己mock一个返回值 |
因为它的目标变成:让代码编译通过。
1.2 正确的流程
CLI 是需求的提出者,不是 API 的被动消费者:
1 | CLI想法 → CLI需求分析 → CLI功能点设计 → CLI需要什么能力 |
CLI 是需求的提出者,不是 API 的被动消费者。
二、CLI 对接静态库:调用链与额外设计
2.1 静态库本身的流程(被调用方)
1 | 需求 → 功能设计 → 接口设计(API) → 模块设计 → 实现 → 生成静态库(.lib/.a) → 提供头文件+库文件 |
产物:
1 | SDK/ |
静态库关注:提供什么能力、输入输出是什么、生命周期如何管理、错误如何返回。
2.2 CLI 对接静态库的调用链
CLI 本质是:把用户输入转换成静态库 API 调用。
1 | 用户命令 |
例如 tool.exe scan test.bin:
1 | main() |
2.3 CLI 需要额外设计什么(静态库没有的问题)
- 命令设计:
tool.exe <command> <options>,类似 git commit / git push / git pull
1 | tool init / scan / dump / export / status |
- 参数模型:
1 | tool scan test.exe --mode fast |
1 | struct CommandArgs { string file; Mode mode; }; |
- 生命周期转换:静态库 Initialize/Execute/Shutdown → CLI 的 main 把一次命令执行映射成库生命周期
1 | int main() { Parse(); Init(); Execute(); Cleanup(); return 0; } |
错误转换:静态库返回 ERROR_MEMORY_FAILED → CLI 显示可读错误并
return 1;输出格式:人看
Scan completed. Found 3 issues.;机器看tool scan xxx --json输出 JSON
2.4 推荐工程结构
1 | Project |
依赖方向:CLI → Core API → Static Library,Core 永远不知道 CLI 存在。
三、CLI 内部的分层:五层结构
CLI 虽然简单,但不要直接 main.cpp 调库,否则以后会失控:
1 | CLI |
1. Presentation Layer(表现层)
处理用户看到的东西:输出文字、颜色、格式、JSON 输出、错误提示。
1 | ConsolePrinter / JsonFormatter / TableFormatter |
禁止:调用核心算法。
2. Command Layer(命令层)
把命令映射到操作。tool scan xxx.exe → ScanCommand。
1 | Command: ScanCommand / ExportCommand / StatusCommand |
职责:命令注册、参数绑定、调度。不负责业务。
3. Application Layer(应用流程层)
CLI 最重要的一层,负责 CLI 这个应用自己的业务流程:
1 | ScanApplication |
4. Adapter Layer(适配层)
连接静态库:API 转换、数据转换、生命周期管理。把静态库的 EngineResult 转换成 CLIResult。
5. Static Library
CLI 不能进入这里。
职责边界表
| 层 | 负责 | 禁止 |
|---|---|---|
| Presentation | 显示 | 业务逻辑 |
| Command | 命令解析 | 算法 |
| Application | CLI 流程 | 核心计算 |
| Adapter | API 转换 | 实现业务 |
| Library | 核心能力 | 知道 CLI |
四、交互式与无交互 CLI:统一框架
CLI 有两种形态,但不是两套流程。
交互式 CLI(有状态)
1 | tool.exe |
1 | Shell → Command Loop → Application Context |
无交互 CLI(一次执行)
1 | tool.exe scan test.exe |
1 | main() → parse args → execute → exit |
共同点与区别
两者都是:
1 | CLI Layer → Application Layer → Adapter → Static Library |
区别只是 Command Layer。所以统一为 CLI Application Framework:
1 | CLI |
交互式 CLI 和无交互 CLI 不是两套流程,只是 Command Layer 不同。
五、API 缺口审查:防 AI 造假
5.1 问题的本质
当需求和接口不匹配时,AI 倾向于补全,而不是停止。
例如需求「导出报告」,已有 API GetResult(),没有 Export(),错误的 AI 会:
1 | void Export() { // generate fake report } |
编译成功,但系统错误。
5.2 API 能力匹配审查(API Gap Report)
位置:
1 | CLI功能设计 → CLI API需求分析 → API匹配审查 → 是否满足? |
输出 API Gap Report:
1 | 需求: 导出报告 |
不能进入编码。
5.3 缺口怎么处理:回跳静态库流程
发现缺口后,不是 CLI 解决,而是生成「静态库需求变更」,重新进入静态库流程:
1 | CLI需求 → API能力分析 |
结合 AI 流程,需要增加一种状态:阻塞。
1 | CLI生成状态: 阻塞 |
六、CLI 驱动静态库演化:双向闭环
6.1 为什么 CLI 会反向影响静态库
因为 CLI 是第一个真实使用者。静态库设计阶段只能假设,例如设计 bool Process(Data input); 看起来没问题,但 CLI 做 tool process file.bin --verbose 时发现需要获取执行进度、错误原因、统计信息:
1 | 已有 API: Process() |
这不是 CLI 的问题,而是静态库抽象能力不足——回到静态库流程。操作系统 API 也是这样演化的:应用发现需要异步 IO、线程、网络,然后推动 OS API 扩展。
6.2 两个方向
传统理解(错误):
1 | Library → Application |
成熟系统(正确):
1 | Application需求 → Library能力演化 → Application实现 |
6.3 更深的架构视角
- 静态库流程偏 Bottom capability design(先设计能力)
- CLI 流程偏 Top usage design(从用户行为反推能力)
两者结合:
1 | 用户场景 → 应用需求 → 能力需求 → API → 实现 |
这比单纯自顶向下更接近真实工程。
七、完整流程:从不成形想法开始
7.1 起点修正
CLI 不应该一开始就假设用户知道「我要做 CLI」。起点是:
1 | 用户不成形想法 → 分析 → 确定是否需要CLI程序 → CLI需求 |
例如用户输入:「我想做一个工具分析游戏数据,可以扫描文件,然后显示结果。」——此时不是「创建 CLI」,而是分析后判断程序类型是 CLI 工具。
7.2 CLI 程序开发流程(完整版)
一、分析阶段
| 步骤 | 输入 | 输出 |
|---|---|---|
| 想法收集 | 用户不成形想法文档 | 想法分析文档(不限定程序类型) |
| 领域分析 | 想法分析文档 | 领域模型文档 |
| 程序类型判断 | 领域模型文档 | 程序类型分析文档(CLI/GUI/服务/库) |
| CLI需求分析 | 程序类型分析文档、领域模型文档 | CLI需求分析文档 |
| 需求点设计 | CLI需求分析文档 | CLI需求点设计文档(拆分功能) |
二、外部能力分析阶段
| 步骤 | 输入 | 输出 |
|---|---|---|
| 业务流程分析 | CLI需求点设计文档 | CLI业务流程分析文档 |
| 业务流程图设计 | CLI业务流程分析文档 | CLI业务流程图 |
| 功能点设计 | CLI业务流程图 | CLI功能点设计文档 |
| 功能流程分析 | CLI功能点设计文档 | CLI功能分析文档 |
| CLI能力需求分析 | CLI功能分析文档 | CLI能力需求列表(不是 API 列表!) |
注意这里产生的是 CLI 能力需求列表,不是 API 列表——因为此时还不知道已有 API。
三、静态库能力匹配阶段
| 步骤 | 输入 | 输出 |
|---|---|---|
| 能力匹配分析 | CLI能力需求列表、已有静态库说明(如果存在) | API能力匹配分析文档 |
| 缺失能力分析 | API能力匹配分析文档 | API缺失需求文档 |
| 静态库流程调用 | API缺失需求文档 | 静态库需求变更文档 |
三种情况:
| 情况 | 处理 |
|---|---|
| 没有静态库 | 进入静态库开发流程 |
| 有静态库且满足 | 继续CLI设计 |
| 有静态库但不足 | 生成缺失需求,返回静态库流程 |
四、CLI 设计阶段
模块设计(Command/Argument/Application/Adapter/Output/Entry)→ 职责和边界设计(CLI:命令、参数、流程、显示、转换;静态库:算法、数据、核心业务)→ 接口设计(CLI接口/数据结构/命令/参数/异常/Adapter)。
五、技术选型
默认:CLI程序 / C++ / Windows / Console / CMake / vcpkg / 依赖静态库。
六、编码实现(分层递进)
| 步骤 | 限制 |
|---|---|
| 目录生成 | 禁止创建文件 |
| 文件生成 | 空文件,禁止填充内容 |
| 类声明生成 | 禁止函数实现 |
| 数据结构生成 | 禁止业务 |
| 接口声明生成 | 禁止函数体 |
| Command实现 | 只处理命令 |
| Application实现 | 不实现核心算法 |
| Adapter实现 | 不能替代静态库 |
| Output实现 | 只负责显示 |
七、代码审查
| 检查 | 失败返回 |
|---|---|
| 层级检查(有没有跨层调用) | 模块设计 |
| 模块检查(跨模块职责) | 模块设计 |
| API使用检查(绕过Adapter) | Adapter设计 |
| 假数据检查(Mock/硬编码) | API匹配分析 |
| 重复实现检查 | 重新划分职责 |
| 规范检查 | 编码阶段 |
八、编译
CLI → StaticLibrary → main.exe,CMake 链接静态库,debug/release 脚本。
九、测试
命令测试、参数测试、流程测试、API集成测试(CLI+静态库)、异常测试。
八、两个流程的统一关系
静态库流程与 CLI 流程不是上下级:
1 | 用户需求 |
1 | 静态库:用户不成形想法 → 需求 → 功能 → API → 实现 |
发现 API 不够 → 跳转静态库流程实现 → 再回来继续 CLI,不仅解决 API 不足问题,还自然形成了双向演化闭环。前面担心的「AI 为了编译成功制造假数据」,本质上就是缺少这个能力缺口反馈回路——一旦加入这个机制,AI 才不会被编译结果绑架。
与其他线的关系
- 与设计起点线:设计起点说「CLI 先设计入口参数」;本篇展开成完整顺序
- 与 06-设计/05:通信程序的设计顺序(协议→组件→接口→实现)与 CLI 的顺序是同一原则(先外后内)在两类程序上的展开,互为对照
- 与系统设计线(06-设计/02):CLI 是系统的一个入口(Adapter);CLI 如何调用静态库是架构问题
- 与 07-工程控制/02:CLI 流程的编码步骤(目录→文件→声明→实现)就是「过程与流程」里逐层递进的实例
- 与 业务分析/08:领域模型逆向展开成 CLI 流程步骤链
- 与 07-工程控制/06-验证:CLI 的测试(命令/参数/流程/集成)是验证线在 CLI 上的落地
收束
1 | CLI 不是 API 包装器,而是需求的提出者 |
