设计原则与代码评审:如何在Code Review中检查设计原则,常见违反案例
代码评审这件事,很多团队都在做。但说实话,大部分Code Review都停留在「变量命名对不对」、「有没有内存泄漏」、「逻辑有没有bug」这个层面。真正能上升到设计原则层面的评审,少之又少。
我个人习惯是,每次Review代码时,脑子里会过一遍「这代码违反了哪条设计原则?」。你想想看,如果每次都能这么想,团队的代码质量怎么可能上不去?
今天我们就聊聊,怎么在Code Review中检查设计原则,以及那些我见过的、最常见的违反案例。
核心观点:Code Review是落地设计原则的最佳战场。没有Review,原则就是纸上谈兵。
一、Code Review中检查设计原则的四个层次
我在项目中遇到过很多次,开发人员提交的代码功能完全正确,但设计上却是一团糟。怎么系统地检查?我总结了一个四层检查法:
| 层次 | 检查重点 | 对应原则 |
|---|---|---|
| 第一层:结构 | 类是否过大?职责是否单一? | 单一职责原则(SRP) |
| 第二层:依赖 | 高层是否依赖低层?是否面向接口? | 依赖倒置原则(DIP) |
| 第三层:扩展 | 新增功能是否需要修改现有代码? | 开闭原则(OCP) |
| 第四层:替换 | 子类能否替换父类? | 里氏替换原则(LSP) |
嗯,这里要注意:不是每次Review都要把四个层次全过一遍。根据变更范围,挑重点看就行。
二、常见违反案例:我踩过的坑
案例1:违反单一职责原则(SRP)
这是最常见的。我曾经Review过一个「报表生成器」的代码,一个类干了三件事:读取数据、格式化报表、发送邮件。你想想看,哪天邮件服务器换了,这个类要改;报表格式变了,也要改;数据源换了,还要改。
// ❌ 违反SRP:一个类承担了太多职责
class ReportGenerator {
public:
void generate() {
fetchData(); // 职责1:数据获取
formatReport(); // 职责2:报表格式化
sendEmail(); // 职责3:邮件发送
}
};
// ✅ 符合SRP:每个类只做一件事
class DataFetcher { /* 只负责获取数据 */ };
class ReportFormatter { /* 只负责格式化 */ };
class EmailSender { /* 只负责发送邮件 */ };
检查技巧:如果看到一个类的描述里出现了「和」、「以及」、「同时」这些词,大概率是违反SRP了。比如「这个类负责读取配置和初始化日志」——拆!
案例2:违反开闭原则(OCP)
我记得有一次,团队要新增一种支付方式。结果发现,支付处理类里有一个巨大的switch-case,每新增一种支付方式,就要改这个类。这就是典型的违反OCP。
// ❌ 违反OCP:新增支付方式需要修改现有代码
class PaymentProcessor {
public:
void process(const std::string& type) {
if (type == "alipay") { /* ... */ }
else if (type == "wechat") { /* ... */ }
// 新增支付方式?继续加else if!
}
};
// ✅ 符合OCP:通过扩展实现新增
class PaymentMethod {
public:
virtual void pay() = 0;
};
class Alipay : public PaymentMethod { /* ... */ };
class WechatPay : public PaymentMethod { /* ... */ };
避坑指南:我曾经在Review时看到一个大神写的代码,用模板特化实现了OCP。看起来很酷,但团队成员看不懂。记住:设计原则是为了让代码更好维护,不是为了炫技。
案例3:违反依赖倒置原则(DIP)
说白了,就是高层模块直接依赖了低层模块。我见过最典型的例子:业务逻辑层直接依赖了MySQL的API。哪天要换成PostgreSQL,整个业务层都得改。
// ❌ 违反DIP:高层依赖低层
class UserService {
private:
MySQLRepository repo; // 直接依赖具体实现
};
// ✅ 符合DIP:高层依赖抽象
class UserService {
private:
IUserRepository& repo; // 依赖接口
};
三、Code Review中的检查清单
我建议团队在Review时,可以对照这个清单快速过一遍:
- 类的大小:一个类超过300行?考虑拆分。
- 函数参数:超过3个参数?考虑封装成结构体。
- 条件分支:看到switch或if-else超过3个分支?考虑多态。
- 继承深度:继承层次超过3层?考虑组合。
- 循环依赖:A依赖B,B又依赖A?这是设计坏味道。
- 硬编码:字符串、数字直接写在代码里?提取成常量或配置。
我的经验:每次Review,至少能发现1-2个违反设计原则的地方。如果一次Review什么都没发现,要么是代码真的完美,要么是你没认真看——后者更常见。
四、设计原则检查的SVG流程图
下面这张图,是我在团队内部培训时用的。它展示了Code Review中检查设计原则的完整流程:
五、如何推动团队落地
光有方法还不够,还得让团队愿意用。我分享几个实战经验:
- 建立Checklist模板:把上面那个清单做成PR模板的一部分,提交代码时自动带出来。
- 先抓大放小:刚开始不要追求完美,先抓最明显的违反案例。比如一个类干了五件事,这种一眼就能看出来。
- 用案例说话:Review时不要只说「这违反了XX原则」,要解释「如果不改,以后加功能会多痛苦」。我一般会举一个以前踩过的坑。
- 渐进式改进:不要指望一次Review把所有问题都修完。每次改一点,代码质量是慢慢提升的。
小技巧:我习惯在Review评论里加一个标签,比如「[SRP] 这个类建议拆分」或者「[OCP] 这里用策略模式替代switch」。时间长了,团队看到标签就知道是设计原则问题。
设计原则的落地,从来不是靠一次培训或者一份文档就能搞定的。它需要你在每一次Code Review中,一点一点地坚持。嗯,这条路没有捷径,但走通了,团队的代码质量会有质的飞跃。