从餐厅后厨到代码世界:聊聊“三层架构”和“高内聚低耦合”
一、先讲个故事:餐厅里的“三层架构”
假设你开了一家餐厅,后厨有三个核心角色:
- 服务员:负责接待顾客、记录订单、端菜上桌。
- 厨师:根据订单烹饪菜品,控制火候、调味。
- 采购员:每天去菜市场买菜,保证食材新鲜。
他们的分工有什么好处?
- 服务员不需要懂怎么炒菜,厨师不需要关心顾客长什么样,采购员只管买对食材。
- 如果某天换了个新厨师,服务员和采购员的工作完全不受影响。
- 如果采购员发现土豆涨价了,他可以直接换成红薯,厨师只需调整菜谱,不需要通知服务员。
这就是现实中的“三层架构”和“高内聚低耦合”
二、什么是服务器中的“三层架构”?
在软件开发中,服务器代码的“三层架构”和餐厅分工异曲同工:
层级 | 职责 | 类比餐厅角色 |
---|---|---|
表现层 | 接收用户请求,返回结果(如网页、JSON) | 服务员 |
业务逻辑层 | 处理核心业务规则(如计算订单价格) | 厨师 |
数据访问层 | 操作数据库(增删改查) | 采购员 |
举个例子:用户下单的流程
- 表现层:用户点击“提交订单”,接收订单数据(商品ID、数量)。
- 业务逻辑层:计算总价、检查库存、生成订单号。
- 数据访问层:将订单保存到数据库,更新库存数量。
代码示例(伪代码):
// 表现层(Controller)
@PostMapping("/order")
public String createOrder(OrderRequest request) {
orderService.createOrder(request); // 调用业务层
return "下单成功!";
}
// 业务逻辑层(Service)
public void createOrder(OrderRequest request) {
double totalPrice = calculateTotalPrice(request); // 计算价格
checkInventory(request); // 检查库存
orderDao.saveOrder(request, totalPrice); // 调用数据层
}
// 数据访问层(Dao)
public void saveOrder(OrderRequest request, double totalPrice) {
String sql = "INSERT INTO orders (...) VALUES (...)";
jdbcTemplate.update(sql); // 操作数据库
}
三、什么是“高内聚低耦合”?
1. 高内聚:一个模块只做一件事
- 厨师只负责炒菜:不会一边切菜一边接待顾客。
- 业务逻辑层只处理规则:不直接操作数据库,也不生成网页。
反面教材:
如果服务员既点菜又炒菜,后厨会乱成一锅粥!
2. 低耦合:模块之间“保持距离”
- 服务员和厨师通过“订单”沟通:服务员不需要知道厨师的手机号。
- 业务逻辑层和数据访问层通过接口交互:换数据库(如MySQL→PostgreSQL)时,只需修改数据层代码。
反面教材:
如果厨师直接去菜市场买菜,采购员就失业了,而且菜价上涨时全店都得停业调整!
四、为什么需要这些设计原则?
1. 改需求时少加班
- 老板说:“用户下单后要发短信通知!”
你只需在业务逻辑层加一行发送短信的代码,其他层完全不用动。
2. 新人接手不崩溃
- 新人只需要看懂某一层的代码(比如数据访问层),就能快速上手改Bug。
3. 代码像乐高积木
- 如果想给系统加个“数据分析模块”,可以直接复用业务逻辑层和数据访问层,无需重写。
五、实际开发中的“踩坑”指南
坑1:把业务逻辑写在Controller(表现层)
- 后果:其他模块想复用这段逻辑时,只能复制粘贴代码。
- 正确做法:Controller只负责接收参数和返回结果,核心逻辑交给Service层。
坑2:数据库SQL满天飞
- 后果:换数据库时,需要全局搜索替换SQL语句。
- 正确做法:将所有数据库操作封装在Dao层,业务层只调用接口。
坑3:层与层之间“直接传数据库对象”
- 后果:数据库表结构变动时,所有层都得跟着改。
- 正确做法:层之间通过DTO(数据传输对象) 或领域模型交互,隐藏底层细节。
六、总结:好的代码像一家好餐厅
- 分工明确:服务员、厨师、采购员各司其职。
- 高效协作:通过订单传递需求,互不干扰。
- 灵活应变:换厨师、换食材、换装修,餐厅照样运转。
记住:
- 写代码时,先问自己:“这个类/方法应该属于哪一层?”
- 修改代码时,先问自己:“这个改动会影响其他模块吗?”