首页/开发/代码质量与评审速查/关注点分离(Separation of Concerns)
设计原则与模式62 条

关注点分离(Separation of Concerns)

把不同关注点(表现、业务、持久化、横切能力)分开组织:Dijkstra 提出的设计视角,MVC、分层、AOP 都是它的具体形态;关注点边界与团队边界常一致。

关注点分离是什么

Dijkstra 在 1974 年提出:能同时把注意力集中在少数几件事上,是处理复杂性的前提。落到代码里就是把表现、业务规则、持久化与横切能力分别组织,让一个关注点变化时不必强迫其它关注点跟着改。它不是某一种架构,而是这些架构共同的判断依据:这条边界到底挡住了哪一类变化。

基本信息

提出Edsger W. Dijkstra(1974)
典型形态分层、MVC、模块化、AOP、六边形
收益各关注点可独立理解、替换、测试
边界关注点怎么切分随领域而变,没有统一分法
历史MVC 源自 Smalltalk-80 的界面实践,后被 Web 框架广泛沿用

典型使用场景

  • Web 层把参数解析与业务规则分开,换接口协议不动规则
  • 持久化细节收进仓储实现,业务代码里不出现 SQL 与连接管理
  • 日志、鉴权、事务抽成切面或中间件,业务方法只留业务
  • 前端把状态管理与渲染分开,换视图库不动数据流
  • 构建脚本把依赖解析与发布动作分阶段,便于单独重跑
  • 报表系统把取数、口径计算与呈现分开:换图表不改口径

横切关注点抽离

横切关注点抽离
// 之前:同一段事务与日志在 20 个 service 方法里各写一遍
public void transfer(Account from, Account to, Money m) {
    log.info("start");
    tx.begin();
    doTransfer(from, to, m);   // 真正的业务只有这一行
    tx.commit();
}

// 之后:横切能力搬到切面,方法体只剩业务语义
@Transactional
@Loggable
public void transfer(Account from, Account to, Money m) {
    doTransfer(from, to, m);
}

形态与分离对象

形态分离的关注点常见实现
分层表现 / 业务 / 持久化Controller-Service-Repository
MVC输入控制 / 数据 / 渲染框架路由与模板引擎
AOP横切能力切面、中间件、装饰器
六边形领域逻辑 / 外部适配端口与适配器
事件驱动触发方 / 处理方消息中间件与订阅者
关注点边界常与团队边界重合:谁维护什么,往往决定代码里怎么切。组织调整后代码边界迟早跟着动,提前对齐能少返工;反过来,如果两个关注点由同一批人维护、改动节奏也一样,强行拆开只会增加同步成本。
分离不等于切碎。每多一层就多一次跳转,追一个请求要跨五层说明切过头了。判断标准是这条边界挡住了哪类变化、省下了多少联动修改,而不是层数或目录数好看。

易错点

  • 为分离而分离:样板代码翻倍,一个简单查询要穿过五层才看到 SQL
  • 横切关注点散落各处:日志格式与事务边界在每个方法里各写一遍,口径很快分叉
  • 按技术分层却不看变化原因:同一个业务规则被切到两个模块,改一次要动两处
  • 关注点定义不清:把「用户相关」当成一个关注点,注册、鉴权、偏好设置于是挤进同一个类

数据来源与参考

  • Edsger W. Dijkstra(1974)关于关注点分离的论述
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌