架构原则概述:高内聚低耦合、关注点分离、DRY原则、KISS原则、YAGNI原则
聊到架构原则,我脑子里第一个蹦出来的词就是「内聚与耦合」。这两个词,说白了就是衡量代码质量的一把尺。你想想看,一个模块内部的东西是不是都围绕同一件事?模块之间是不是尽量少说话?这就是高内聚、低耦合的核心。
高内聚与低耦合
高内聚,指的是一个模块内部的元素之间关联紧密,共同完成一个明确的功能。低耦合,指的是模块之间的依赖关系尽量弱,一个模块的变化对其它模块的影响尽可能小。
我在项目中遇到过这样一个场景:一个订单模块里混了用户登录校验、商品库存查询、支付回调处理。结果每次改支付逻辑,都要重新测试整个订单模块。这就是典型的内聚不够。后来我把支付逻辑抽出去,订单模块只负责订单状态流转,问题就解决了。
核心要点:
- 高内聚:模块内部职责单一,改动集中在一个小范围内
- 低耦合:模块之间通过接口通信,不直接依赖内部实现
举个例子,一个用户服务模块,它只负责用户的注册、登录、信息查询。它不应该知道订单模块怎么处理支付。这就是高内聚。如果用户服务直接调用了订单服务的数据库表,那就是耦合过紧。
// 高内聚示例
class UserService {
public function getUserById($id) {
// 只做用户查询
return $this->userRepository->find($id);
}
}
// 低耦合示例
class OrderService {
private $userService;
public function __construct(UserService $userService) {
$this->userService = $userService; // 依赖接口,不依赖具体实现
}
public function createOrder($userId, $productId) {
$user = $this->userService->getUserById($userId);
// 创建订单逻辑
}
}
我的经验:判断内聚够不够,有个简单方法:如果你改一个功能需要改三个以上的文件,那大概率内聚出了问题。
关注点分离
关注点分离,英文叫 Separation of Concerns。说白了,就是把不同的事情交给不同的模块去处理。你写代码的时候,不要在一个函数里既处理业务逻辑,又处理数据库操作,还处理界面渲染。
我记得有一次接手一个老项目,一个控制器方法有 300 多行,里面混杂着 SQL 查询、HTML 拼接、权限校验。改一个需求,要翻半天才能找到对应的代码。这就是典型的关注点没有分离。
关注点分离的好处很明显:
- 每个模块只关心自己的事,逻辑清晰
- 修改一个关注点,不影响其它关注点
- 便于单元测试,每个模块可以独立测试
常见的实现方式就是分层架构:表现层、业务层、数据访问层。每一层只处理自己该处理的事。
注意:关注点分离不是越细越好。我曾经见过一个项目分了 8 层,结果每层之间传递数据要写一堆转换代码,反而增加了复杂度。适度分离,够用就好。
DRY 原则
DRY 是 Don't Repeat Yourself 的缩写。意思就是不要重复你自己。这个原则听起来简单,但实际做起来并不容易。
我见过很多代码里,同样的校验逻辑在三个地方各写了一遍。后来需求变了,改了一个地方,另外两个没改,线上就出了 bug。这就是重复代码的代价。
DRY 原则的核心是:每一段知识在系统中只应该有一个权威的表示。也就是说,同样的逻辑,只写一次,然后复用。
// 违反 DRY 原则
function validateEmail1($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
function validateEmail2($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
// 符合 DRY 原则
function validateEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
避坑指南:我曾经为了追求 DRY,把两个看似相同但实际语义不同的逻辑强行合并成一个函数。结果后来需求分化,那个函数变得臃肿不堪。DRY 的前提是「同一件事」,而不是「看起来像」。
KISS 原则
KISS 是 Keep It Simple, Stupid 的缩写。翻译过来就是「保持简单,傻瓜」。这个原则强调:简单是软件设计的最高追求。
你想想看,一个复杂的系统,维护成本高、出 bug 的概率大、新人上手慢。而简单的系统,一眼就能看懂,改起来也放心。
我见过太多过度设计的案例了。一个简单的 CRUD 系统,非要引入事件驱动、CQRS、领域事件。结果项目延期,代码没人能看懂。其实很多时候,一个简单的 MVC 就够了。
KISS 原则的实践建议:
- 能用数组解决的问题,不要引入对象
- 能用 if-else 解决的问题,不要引入设计模式
- 能用单表解决的问题,不要引入分库分表
我的观点:简单不是简陋,而是恰到好处。一个简单的系统,应该能清晰地表达它的意图,而不是让读者去猜。
YAGNI 原则
YAGNI 是 You Aren't Gonna Need It 的缩写。意思是「你其实不需要它」。这个原则告诉我们:只实现当前需要的功能,不要为未来可能需要的功能提前设计。
为什么?因为未来的需求很可能和你想的不一样。你花了两周时间设计了一个通用的扩展框架,结果需求来了,你的框架根本用不上,还得重写。
我曾经在一个项目中,提前设计了一个「万能配置中心」,支持多种数据源、多种格式、多种缓存策略。结果项目上线一年,只用到了最简单的 JSON 文件配置。那套复杂的框架,成了没人敢动的遗留代码。
警告:YAGNI 不是让你不做设计。合理的架构设计是必要的,但不要为「可能」的需求过度设计。记住:你不需要为永远不会来的需求买单。
知识体系总览
下面这张图,我把这五个原则的关系梳理了一下。你可以看到,它们其实是从不同角度在追求同一个目标:让代码更容易理解、更容易修改、更容易维护。
这五个原则,其实是一个整体。高内聚低耦合是结构上的要求,关注点分离是职责上的划分,DRY 是避免重复,KISS 是控制复杂度,YAGNI 是防止过度设计。它们互相补充,缺一不可。
在实际项目中,我建议你从 KISS 和 YAGNI 入手。先把当前需求做简单,不要想太多。然后逐步引入关注点分离和高内聚低耦合。DRY 原则则贯穿始终,时刻提醒自己不要写重复代码。
总结一下:
- 高内聚低耦合:模块内部紧,模块间松
- 关注点分离:不同的事交给不同的模块
- DRY:同样的逻辑只写一次
- KISS:简单是最高级的复杂
- YAGNI:不为未来不确定的需求买单
嗯,这些原则说起来简单,但真正用好需要大量的实践。我建议你在写每一行代码之前,都问自己一句:这个设计够简单吗?这个逻辑有没有重复?这个模块的职责是不是太杂了?慢慢养成习惯,你的代码质量会有质的提升。