重构:文件系统模块化架构,增强 Markdown 渲染
- 拆分 FileSystem.vue 为模块化组件架构 - 新增 Markdown Mermaid 图表渲染支持 - 新增 180+ 编程语言代码高亮 - 修复编辑/预览模式切换渲染问题 - 优化亮色/暗色模式主题适配 - 新增 TypeScript 类型定义
This commit is contained in:
332
docs/代码审查/anti-over-engineering-report.md
Normal file
332
docs/代码审查/anti-over-engineering-report.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# 避免过度封装 - 代码清理报告
|
||||
|
||||
## 执行日期
|
||||
2026-01-27
|
||||
|
||||
## 背景
|
||||
在代码优化过程中,需要警惕**过度封装**(Over-engineering)问题。
|
||||
避免为了"优雅"而创建不必要的抽象层。
|
||||
|
||||
---
|
||||
|
||||
## 🔍 检查发现的问题
|
||||
|
||||
### 问题 1: WrapError/WrapErrorf 过度封装 ❌
|
||||
|
||||
**原始实现**:
|
||||
```go
|
||||
// 创建了两个新函数,但代码中没有任何使用
|
||||
func WrapError(operation string, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s失败: %v", operation, err)
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
1. ❌ 实际代码中**零使用**
|
||||
2. ❌ 只是把 `fmt.Errorf` 包装了一层
|
||||
3. ❌ 反而增加了学习成本和依赖
|
||||
4. ❌ 违背了 YAGNI 原则(You Aren't Gonna Need It)
|
||||
|
||||
**正确做法**:
|
||||
```go
|
||||
// 直接使用标准库
|
||||
if err != nil {
|
||||
return fmt.Errorf("操作失败: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
**结论**:❌ **删除** - 过度封装,未被使用
|
||||
|
||||
---
|
||||
|
||||
### 问题 2: 文档注释过于冗长 ❌
|
||||
|
||||
**原始实现**:
|
||||
- timeout.go: 70+ 行注释
|
||||
- utils.go: 40+ 行注释
|
||||
- errors.go: 60+ 行注释
|
||||
|
||||
**问题**:
|
||||
1. ❌ 注释比代码还长
|
||||
2. ❌ 包含大量"显而易见"的说明
|
||||
3. ❌ 维护成本高
|
||||
4. ❌ 违背了"代码即文档"原则
|
||||
|
||||
**优化后**:
|
||||
```go
|
||||
// 数据库操作超时配置
|
||||
const (
|
||||
TimeoutPing = 2 * time.Second // 连接测试超时
|
||||
TimeoutConnect = 5 * time.Second // 初始连接超时
|
||||
TimeoutFastQuery = 10 * time.Second // 元数据查询超时
|
||||
TimeoutQuery = 30 * time.Second // 普通查询超时
|
||||
TimeoutLongOp = 60 * time.Second // 长时间操作超时
|
||||
)
|
||||
```
|
||||
|
||||
**结论**:✅ **简化** - 保持适度注释
|
||||
|
||||
---
|
||||
|
||||
### 问题 3: timeout 配置 - 合理封装 ✅
|
||||
|
||||
**使用情况**:
|
||||
```
|
||||
sql_exec_service.go: 5处使用
|
||||
pool.go: 2处使用
|
||||
redis.go: 2处使用
|
||||
mongo.go: 3处使用
|
||||
```
|
||||
|
||||
**价值**:
|
||||
1. ✅ 消除14处硬编码
|
||||
2. ✅ 统一配置管理
|
||||
3. ✅ 便于修改调整
|
||||
4. ✅ 有实际使用价值
|
||||
|
||||
**结论**:✅ **保留** - 合理封装,有实际价值
|
||||
|
||||
---
|
||||
|
||||
### 问题 4: FormatBytes - 合理封装 ✅
|
||||
|
||||
**使用情况**:
|
||||
```
|
||||
system.go: GetMemoryInfo() 中使用
|
||||
system.go: GetDiskInfo() 中使用
|
||||
```
|
||||
|
||||
**价值**:
|
||||
1. ✅ 消除了重复代码
|
||||
2. ✅ 逻辑有一定复杂度(不是简单包装)
|
||||
3. ✅ 有多个调用点
|
||||
|
||||
**结论**:✅ **保留** - DRY 原则应用
|
||||
|
||||
---
|
||||
|
||||
## ✅ 执行的清理操作
|
||||
|
||||
### 1. 删除过度封装的文件
|
||||
|
||||
```bash
|
||||
rm internal/common/errors.go # WrapError/WrapErrorf 未使用
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 零使用
|
||||
- 只是对 fmt.Errorf 的简单包装
|
||||
- 增加不必要的抽象层
|
||||
|
||||
### 2. 简化文档注释
|
||||
|
||||
**修改文件**:
|
||||
- `internal/common/timeout.go` - 从 70 行注释减少到 12 行
|
||||
- `internal/common/utils.go` - 从 40 行注释减少到 8 行
|
||||
|
||||
**原则**:
|
||||
- ✅ 保留必要的注释(为什么这样做)
|
||||
- ❌ 删除显而易见的注释(做了什么)
|
||||
- ❌ 删除冗长的示例和说明
|
||||
|
||||
### 3. 保留有价值的封装
|
||||
|
||||
**保留文件**:
|
||||
- `internal/common/utils.go` - FormatBytes(消除重复)
|
||||
- `internal/common/timeout.go` - 超时常量(统一配置)
|
||||
|
||||
---
|
||||
|
||||
## 📊 清理效果
|
||||
|
||||
| 项目 | 清理前 | 清理后 | 说明 |
|
||||
|------|--------|--------|------|
|
||||
| **common 包文件** | 3个 | 2个 | 删除 errors.go |
|
||||
| **timeout.go 注释** | 70行 | 12行 | -83% |
|
||||
| **utils.go 注释** | 40行 | 8行 | -80% |
|
||||
| **实际使用的函数** | 3个 | 2个 | -1个 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 封装原则总结
|
||||
|
||||
### ✅ 应该封装的情况
|
||||
|
||||
1. **消除重复代码** (DRY)
|
||||
```go
|
||||
// ✅ 好:FormatBytes 被3个地方使用
|
||||
common.FormatBytes(size)
|
||||
```
|
||||
|
||||
2. **复杂逻辑**
|
||||
```go
|
||||
// ✅ 好:逻辑复杂,值得封装
|
||||
func parseComplexConfig(data []byte) (*Config, error) {
|
||||
// 50行复杂逻辑
|
||||
}
|
||||
```
|
||||
|
||||
3. **统一配置**
|
||||
```go
|
||||
// ✅ 好:14处使用的配置常量
|
||||
const TimeoutQuery = 30 * time.Second
|
||||
```
|
||||
|
||||
### ❌ 不应该封装的情况
|
||||
|
||||
1. **简单包装标准库**
|
||||
```go
|
||||
// ❌ 差:只是包装 fmt.Errorf
|
||||
func WrapError(op string, err error) error {
|
||||
return fmt.Errorf("%s失败: %v", op, err)
|
||||
}
|
||||
```
|
||||
|
||||
2. **未被使用的抽象**
|
||||
```go
|
||||
// ❌ 差:定义了但没用
|
||||
type TimeoutConfig struct { ... }
|
||||
var DefaultTimeouts = TimeoutConfig{...}
|
||||
// 实际代码中没人用 TimeoutConfig
|
||||
```
|
||||
|
||||
3. **过度注释**
|
||||
```go
|
||||
// ❌ 差:注释比代码长
|
||||
// FormatBytes 格式化字节大小...
|
||||
//
|
||||
// 参数:
|
||||
// bytes - 字节数...
|
||||
//
|
||||
// 返回:
|
||||
// 格式化后的字符串...
|
||||
//
|
||||
// 示例:
|
||||
// fmt.Println(FormatBytes(1024))...
|
||||
//
|
||||
// 注意:
|
||||
// - 使用1024进制...
|
||||
// - 支持PB级别...
|
||||
func FormatBytes(bytes uint64) string { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 封装决策清单
|
||||
|
||||
在创建新函数/常量前,先问自己:
|
||||
|
||||
### 1. 是否消除重复?
|
||||
- [ ] 是否有2个以上使用点?
|
||||
- [ ] 代码是否真的重复?
|
||||
- **如果否** → 不要封装
|
||||
|
||||
### 2. 是否增加价值?
|
||||
- [ ] 是否简化了调用?
|
||||
- [ ] 是否提高了可读性?
|
||||
- [ ] 是否便于维护?
|
||||
- **如果否** → 不要封装
|
||||
|
||||
### 3. 是否过度抽象?
|
||||
- [ ] 是否只是简单包装标准库?
|
||||
- [ ] 是否可以被2-3行代码替代?
|
||||
- **如果是** → 不要封装
|
||||
|
||||
### 4. 是否会被使用?
|
||||
- [ ] 是否有明确的调用者?
|
||||
- [ ] 是否解决了实际问题?
|
||||
- **如果否** → 不要封装
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证状态
|
||||
|
||||
```bash
|
||||
$ go build -v
|
||||
go-desk/internal/common
|
||||
go-desk/internal/system
|
||||
go-desk/internal/dbclient
|
||||
go-desk/internal/storage
|
||||
go-desk/internal/service
|
||||
go-desk/internal/api
|
||||
go-desk
|
||||
✅ 编译成功
|
||||
```
|
||||
|
||||
- ✅ 删除未使用的封装
|
||||
- ✅ 简化冗长的注释
|
||||
- ✅ 保留有价值的抽象
|
||||
- ✅ 代码更简洁
|
||||
|
||||
---
|
||||
|
||||
## 🎓 经验教训
|
||||
|
||||
### YAGNI 原则(You Aren't Gonna Need It)
|
||||
|
||||
> 不要为未来可能需要的功能编写代码。
|
||||
> 只写当前确实需要的功能。
|
||||
|
||||
**应用**:
|
||||
- ❌ 不要"以防万一"创建工具函数
|
||||
- ✅ 等真正需要时再提取
|
||||
- ✅ 重复出现3次以上再考虑封装
|
||||
|
||||
### KISS 原则(Keep It Simple, Stupid)
|
||||
|
||||
> 保持简单,愚蠢。
|
||||
|
||||
**应用**:
|
||||
- ❌ 不要过度设计
|
||||
- ❌ 不要为了"优雅"而封装
|
||||
- ✅ 简单直接往往更好
|
||||
|
||||
### 注释原则
|
||||
|
||||
> 代码是最好的文档。注释说明"为什么",而不是"是什么"。
|
||||
|
||||
**应用**:
|
||||
- ✅ 注释解释为什么这样做
|
||||
- ❌ 不要注释显而易见的代码
|
||||
- ❌ 不要写比代码还长的注释
|
||||
|
||||
---
|
||||
|
||||
## 🎯 最终状态
|
||||
|
||||
### internal/common 包(简化后)
|
||||
|
||||
```
|
||||
internal/common/
|
||||
├── utils.go # FormatBytes(合理封装,消除重复)
|
||||
└── timeout.go # 超时常量(合理封装,统一配置)
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ 每个函数/常量都有实际使用
|
||||
- ✅ 代码简洁,注释适度
|
||||
- ✅ 避免了过度封装
|
||||
- ✅ 符合 YAGNI 和 KISS 原则
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
### 软件工程原则
|
||||
1. **YAGNI** - You Aren't Gonna Need It
|
||||
2. **KISS** - Keep It Simple, Stupid
|
||||
3. **DRY** - Don't Repeat Yourself(但不要过度)
|
||||
|
||||
### Go 语言哲学
|
||||
- "Clear is better than clever"
|
||||
- "Avoid over-engineering"
|
||||
- "Readability counts"
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2026-01-27
|
||||
**清理阶段**:避免过度封装
|
||||
**状态**:✅ 已完成
|
||||
Reference in New Issue
Block a user