06 · 组件化与演进(Components & Evolution)

📅 预计 60 分钟 | ⭐ = 重要知识点 | 📌 中英术语见文末


6.0 先给直觉:系统不是一次建成的

软件是慢慢长大的:今天加个功能,明天换个数据库,后天拆个大模块。这门课收尾讲三件事:怎么把系统拆成组件、怎么定义清晰的接口、怎么在可维护性 / 性能 / 成本之间权衡,以及架构怎么随需求演进而不腐烂。

6.1 组件与接口 ⭐

组件是"黑盒子",接口是"插口"

组件(component)是系统中可独立替换、独立部署的功能单元。接口(interface)是组件对外承诺的"我能干什么、你要怎么用我"的约定。像打印机:它是组件,USB 口是接口——你只需要把线插进去,不用知道里面怎么打印。

接口设计要点:

  • 稳定:接口一旦发布,改动会影响所有使用者,尽量少变、保持向后兼容。
  • 最小:只暴露必要能力,内部实现细节全部隐藏。
  • 明确:参数、返回值、异常行为写清楚,让使用者无歧义。
class PaymentService:                  # 组件对外只暴露这一个接口
    def pay(self, order_id: int, amount: float) -> bool:
        """返回 True 表示支付成功。调用方只需关心这个约定。"""
        ...                            # 内部怎么对接银行是它的私事

接口就是契约:实现方保证"输入这样的参数,我返回这样的结果",调用方保证"我只按约定使用"。双方基于契约协作,任何一方都可被替换——这正是组件"可插拔"的基础。

⚠️ 常见错误

  1. 接口里暴露实现细节(返回内部对象、要求调用方了解内部状态),一改内部就波及调用方。
  2. 接口频繁变动,使用者被迫跟着改,组件的"可替换"价值被消耗殆尽。

6.2 模块化与内聚耦合 ⭐

拆模块的三条路

模块化是把系统拆成可独立理解、独立测试、独立替换的模块。拆得好不好,用两个老朋友衡量:内聚(模块内部联系紧)和耦合(模块之间依赖松)。

常见拆分方式:

  • 按层拆:表现、业务、数据(对应分层架构)。
  • 按业务能力拆:订单模块、用户模块、库存模块(微服务常用)。
  • 按技术关注点拆:日志、缓存、配置(横切关注点,通常配合 AOP 或中间件)。

判断拆得好不好,可以问一个问题:改一个需求,要动几个模块? 动得越少,模块边界越好。

⚠️ 常见错误

  1. 为了"模块多显得规整"而拆,实际模块之间大量互相依赖,耦合比不拆还高。
  2. 模块之间通过共享全局变量、直接改对方内部数据通信,边界形同虚设,牵一发而动全身。

6.3 架构权衡(Trade-off)⭐

没有最好,只有最合适

软件架构没有银弹,每个选择都是在多个目标之间取舍。几个最常见的权衡:

  • 性能 vs 可维护性:为性能做缓存、冗余数据、写特例代码,往往牺牲可读性与一致性。
  • 简单 vs 灵活:提前引入插件化、配置化机制能应对变化,但复杂度会过早降临。
  • 一致性 vs 可用性:分布式系统里,CAP 定理指出强一致与高可用难以兼得,要按业务取舍。
  • 自研 vs 用现成:自研可控但成本高、维护重;开源省事但受制于人、迁移受牵制。

正确的姿势:先明确最重要的非功能目标(性能敏感、维护成本敏感,还是快速迭代),再选择方案,并清楚记录"我为了什么,放弃了什么"。这样将来做调整时,有据可依。

⚠️ 常见错误

  1. 不看业务场景,盲目追求"最先进"的架构(比如小项目上微服务、全链路异步)。
  2. 优化没有数据支撑——先测量、定位瓶颈,再动手优化,而不是凭感觉"这里应该很慢"。

6.4 架构演进 ⭐

让系统慢慢变好,而不是慢慢烂掉

架构不是一次定型就一劳永逸,它会持续演进。好的演进过程有两个关键词:小步可回退。每次改动尽量小、可验证、能撤销,而不是憋一个大版本一次性推翻。

演进中要防的"架构腐烂"(architecture decay)信号:

  • 修改放大:改一个小需求,牵连一大片文件。
  • 循环依赖:模块之间你依赖我、我依赖你,改动互相波及。
  • 重复代码:同一逻辑散落多处,改一处漏一处。
  • 上帝类 / 上帝模块:某个类或模块越滚越大,什么都知道、什么都敢碰。

常见的演进手法:

  • 重构(refactor):不改功能、只改结构,让代码更清晰。
  • 渐进式替换:旧接口 + 适配器过渡,等新组件稳定再切换,降低一次性替换的风险。
  • 依赖方向治理:持续检查依赖图,及时阻止依赖反向、出现环。

演进的原则:每一次提交都让系统比之前更好理解,而不是更混乱。架构师不是一次性画完蓝图就撒手,而是在长期维护中不断微调方向,让小步改进积累成大收益。

⚠️ 常见错误

  1. 重构与改功能混在一次提交里,出了 bug 分不清是重构引入还是需求引入。
  2. 等架构烂到不能动才想治理,改动成本已经巨大——腐烂信号要早发现、早处理。

📌 术语对照(中英)

中文 English
组件 Component
接口 / 契约 Interface / Contract
模块化 Modularity
权衡 Trade-off
架构腐烂 Architecture Decay
重构 Refactor

🎯 复习清单

  • 组件与接口的关系,为什么说接口是"契约"。
  • 模块化的三种拆分思路,以及"改一个需求动几个模块"的判断法。
  • 架构权衡中,做决定前要明确的非功能目标是什么。
  • 架构腐烂的四个典型信号。
  • 重构与渐进式替换在架构演进中的作用。