《敏捷软件开发》读后:SOLID让我从“能跑就行”变成“敢改”
一、先破除我对“敏捷”的偏见
大一那会儿我对“敏捷”的印象就俩字:开会。站会、需求评审会、复盘会……一圈人围着白板浪费时间。直到大三上学期把《代码整洁之道》啃完,又顺着作者 Uncle Bob(Robert C. Martin)的名字摸到这本《敏捷软件开发:原则、模式与实践》,我才反应过来:敏捷根本不是开会方法论,它是一套教你怎么写出能长期活下去的代码的纪律。
这本书分两半:前半本讲原则(敏捷宣言、XP 实践,以及最重要的 SOLID 五大设计原则),后半本讲实践(测试驱动开发、重构、结对编程),并用一个完整的薪水支付系统案例把原则串起来。今天我不想复述书里那个 payroll 案例(书你该自己读),只想聊聊对我影响最大的几个点,以及它们怎么改变了我写课程项目和那个 ERP 小系统的姿势。
二、SOLID 不是背出来的,是“改不动了”才懂的
第一次听到 SOLID 是在某次面试八股里,当时背得滚瓜烂熟,但一个都用不上——因为我写的代码根本“活”不到需要改第二次。等到了大三,手上的项目从十几行的作业变成几百行甚至上千行的系统(比如我和室友做的那个接了飞书机器人 + DeepSeek 的 ERP 小工具),我才真正被“改一行崩三处”教做人。
2.1 SRP 单一职责:一个函数只该为一个“变化原因”负责
我最常被坑的就是“上帝函数”——一个函数里塞了读配置、拼 SQL、发请求、写日志。书里一句话点醒我:“一个模块应该有且仅有一个被修改的理由。”
改之前(一锅炖):
# ❌ 反例:一个函数干了四件事,任何需求变了都得动它
def process_order(order_id: str):
# 1. 读数据库
conn = sqlite3.connect("erp.db")
row = conn.execute("SELECT * FROM orders WHERE id=?", (order_id,)).fetchone()
# 2. 算金额(含折扣规则)
amount = row["price"] * 0.9 if row["vip"] else row["price"]
# 3. 调飞书机器人通知
requests.post(FEISHU_WEBHOOK, json={"text": f"订单 {order_id} 金额 {amount}"})
# 4. 写日志
print(f"[INFO] processed {order_id}")
改之后,按“变化原因”切开:
# ✅ 每个函数只对一个变化原因负责
def load_order(order_id: str) -> "Order":
with sqlite3.connect("erp.db") as conn:
return Order.from_row(conn.execute("...").fetchone())
def calc_amount(order: "Order") -> float:
return order.price * 0.9 if order.vip else order.price
def notify_feishu(order_id: str, amount: float) -> None:
requests.post(FEISHU_WEBHOOK, json={"text": f"订单 {order_id} 金额 {amount}"})
def process_order(order_id: str):
order = load_order(order_id)
amount = calc_amount(order)
notify_feishu(order_id, amount)
拆完才发现:折扣规则变了只动 calc_amount,通知渠道从飞书换成企业微信只动 notify_feishu,数据库换成 MySQL 只动 load_order。 可维护性不是靠注释写得多,是靠变化被隔离在各处。
2.2 OCP 开闭原则:对扩展开放,对修改关闭
书里强调:好设计要能“加新功能时基本不用改老代码”。最直观的一招就是面向抽象编程。
# ❌ 每次新增通知渠道都要改这个函数
def notify(channel: str, msg: str):
if channel == "feishu": ...
elif channel == "wecom": ...
# ✅ 抽象出 Notifier,新渠道只需加一个类
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def send(self, msg: str): ...
class FeishuNotifier(Notifier):
def send(self, msg: str):
requests.post(FEISHU_WEBHOOK, json={"text": msg})
class WecomNotifier(Notifier):
def send(self, msg: str):
requests.post(WECOM_WEBHOOK, json={"text": msg})
def notify(notifier: Notifier, msg: str):
notifier.send(msg) # 永远不用改
这条和前面那个 ERP 项目最对得上:当时我们硬把飞书、钉钉逻辑 if/else 怼在一起,结果加个企业微信分支差点把通知全搞挂。OCP 就是给这种“迟早要扩展”的地方提前留好口子。
2.3 DIP 依赖倒置:高层不依赖低层,都依赖抽象
这条对我改造“单测跑不起来”的窘境帮助最大。以前业务代码里直接 sqlite3.connect(...),一到单测就得真的连数据库。按 DIP,业务层只认一个 OrderRepository 接口,测试时换内存假实现:
class OrderRepository(ABC):
@abstractmethod
def get(self, order_id: str) -> "Order": ...
class SqliteOrderRepository(OrderRepository): # 生产用
def get(self, order_id: str) -> "Order": ...
class FakeOrderRepository(OrderRepository): # 测试用
def get(self, order_id: str) -> "Order":
return Order(id=order_id, price=100)
依赖倒置的红利不是架构多优雅,是我终于能写单测了。 这一点和书后半本的 TDD 正好闭环。
三、原则和敏捷宣言是配套的,别只学一半
书开头把敏捷宣言又摆了一遍:个体与互动高于流程与工具、可工作的软件高于详尽的文档、客户合作高于合同谈判、响应变化高于遵循计划。
我以前误以为“敏捷”= 不写文档、随便改需求。读完才懂它的真意是把“能响应变化”当成一等公民。SOLID 就是为了让代码“易变”,TDD 是给变化上保险,重构是让变化成本持续走低——它们是一个组合拳,单拎出任何一条都会走形。
四、一点个人结论
这本书我没当“设计模式大全”来读(那本是 GoF),而是当“为什么这样设计才扛造”来读。读完最大的变化不是我会背几个原则了,而是我开始在写每个函数前多问一句:“这块以后会怎么变?变化来了我改几处?”
对还在上这门课的同学一句实话:SOLID 看着像教条,真到你项目第一次返工、第一次加需求改崩了,你会回来感谢它的。它救不了 deadline,但能救你明天的自己。

浙公网安备 33010602011771号