设计决策
本文档采用 Architecture Decision Records (ADR) 风格记录关键技术决策。
ADR 索引
| 编号 | 标题 | 状态 | 日期 |
|---|---|---|---|
| ADR-001 | 选择 Rust 和 Go 作为实现语言 | 已采纳 | 2025-01 |
| ADR-002 | 采用 Monorepo 架构 | 已采纳 | 2025-01 |
| ADR-004 | gzip 采用库 + 二进制模式 | 已采纳 | 2025-02 |
| ADR-005 | htop 使用平台抽象层 | 已采纳 | 2025-03 |
ADR-001: 选择 Rust 和 Go 作为实现语言
状态
已采纳 (2025-01)
背景
本项目需要选择适合系统编程教学的编程语言,要求:
- 能够直接操作系统资源
- 有良好的错误处理机制
- 适合 CLI 工具开发
- 有活跃的社区和丰富的生态
决策
选择 Rust 和 Go 作为实现语言,采用"同一问题双实现"的方式。
理由
Rust 优势:
- 编译期内存安全,无 GC 暂停
- 零成本抽象,性能可预测
- 强类型系统,减少运行时错误
- 适合系统级编程学习
Go 优势:
- 语法简洁,学习曲线平缓
- 内置并发原语 (goroutine, channel)
- 编译速度快,开发迭代快
- 标准库丰富,减少依赖
为什么不选 C/C++:
- C: 内存安全问题显著,不适合教学安全实践
- C++: 复杂度过高,学习曲线陡峭
后果
- 正面:两种语言对比学习,加深理解
- 正面:覆盖不同开发场景和偏好
- 负面:维护两套代码,工作量翻倍
- 负面:需要同时熟悉两种工具链
ADR-002: 采用 Monorepo 架构
状态
已采纳 (2025-01)
背景
需要组织三个工具(dos2unix, gzip, htop)的代码仓库结构。
决策
采用 Monorepo 架构,将所有工具放在同一仓库。
理由
选择 Monorepo 的理由:
- 学习仓库需要统一视角
- 共享 CI/CD 配置
- 共享文档站点
- 原子提交,便于追踪
- 简化克隆和设置
放弃 Polyrepo 的理由:
- 增加管理复杂度
- 工具间依赖不多
- 不适合学习场景
后果
- 正面:统一版本和发布
- 正面:简化 CI/CD
- 负面:仓库体积增长
- 负面:权限粒度粗
ADR-004: gzip 采用库 + 二进制模式
状态
已采纳 (2025-02)
背景
gzip 工具需要决定代码组织方式:纯二进制 vs 库 + 二进制。
决策
采用 库 + 二进制 模式,将核心逻辑放在库中,CLI 专注于参数解析和调用。
理由
好处:
- 库可被其他项目引用
- 核心逻辑易于单元测试
- 清晰的关注点分离
- 符合 Rust 社区惯例
后果
- 正面:代码结构清晰
- 正面:测试更容易
- 负面:文件数量增加
ADR-005: htop 使用平台抽象层
状态
已采纳 (2025-03)
背景
htop 需要支持 Unix 和 Windows 两个平台,系统 API 差异很大。
决策
采用 平台抽象层 模式,通过 trait/interface 定义统一接口,平台特定实现放在单独模块。
理由
Rust 实现:
rust
// 平台无关 trait
pub trait ProcessInfo {
fn pid(&self) -> u32;
fn name(&self) -> &str;
fn cpu_usage(&self) -> f32;
fn memory_usage(&self) -> u64;
}
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;Go 实现:
go
// 平台无关接口
type ProcessInfo interface {
PID() uint32
Name() string
CPUUsage() float32
MemoryUsage() uint64
}
//go:build unix
package process // Unix 实现
//go:build windows
package process // Windows 实现后果
- 正面:核心逻辑统一
- 正面:易于添加新平台
- 负面:需要维护多个实现
- 负面:抽象层有性能开销