用多态替换 switch:让代码更符合开闭原则

副标题 / 摘要 switch 往往会不断膨胀。本文用策略模式把分支逻辑拆分成可扩展的多态结构。 目标读者 需要重构条件分支的工程师 关注可维护性的团队 学习设计模式的开发者 背景 / 动机 分支逻辑一旦增长,switch 会变成维护噩梦。 多态可以把“选择逻辑”变成“可扩展结构”。 核心概念 策略模式:把算法封装为对象 开闭原则:对扩展开放,对修改关闭 多态分发:用对象替代条件分支 实践指南 / 步骤 识别 switch 的分支类型 为每个分支定义策略类 用工厂或映射选择策略 新增分支只新增类 可运行示例 class Formatter: def format(self, text): raise NotImplementedError class FailFormatter(Formatter): def format(self, text): return "error" class OkFormatter(Formatter): def format(self, text): return text + text def get_formatter(response): return {"FAIL": FailFormatter(), "OK": OkFormatter()}.get(response) if __name__ == "__main__": f = get_formatter("OK") print(f.format("hi")) 解释与原理 switch 把逻辑集中在一处,扩展时必须修改旧代码。 多态把分支拆成独立类,新增规则只需新增类。 常见问题与注意事项 小分支是否需要多态? 不一定,只有分支频繁扩展时值得。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

Active Record 的限制与缺陷:为什么它不适合复杂领域

副标题 / 摘要 Active Record 让开发变快,但在复杂领域模型中常会失控。本文解释其限制与替代思路。 目标读者 使用 ORM 的后端工程师 设计领域模型的团队 关注架构演进的技术负责人 背景 / 动机 Active Record 把数据与行为放在同一个类中,适合简单 CRUD。 当业务复杂时,领域逻辑会被持久化细节污染。 核心概念 Active Record:模型自带持久化行为 领域模型污染:业务逻辑与数据访问耦合 事务边界:难以清晰控制 实践指南 / 步骤 识别业务复杂度与规则数量 评估是否需要明确的领域层 复杂场景考虑 Data Mapper 将持久化逻辑下沉到仓储层 可运行示例 # Active Record:模型自带保存逻辑 class User: def __init__(self, name): self.name = name def save(self): # 这里直接访问数据库 return f"save {self.name}" if __name__ == "__main__": u = User("Alice") print(u.save()) 解释与原理 Active Record 的优点是简单直接,但会让业务逻辑与持久化高度耦合。 当规则变复杂时,测试与演进成本显著上升。 常见问题与注意事项 Active Record 真的不适合大型系统吗? 并非绝对,但复杂业务会更难维护。 是否必须迁移到 Data Mapper? 只有在复杂规则与多聚合情况下才建议。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

反腐败层(ACL):如何隔离外部系统的复杂性

副标题 / 摘要 反腐败层用于隔离外部系统的模型与语义污染。本文解释其工程价值与实现策略。 目标读者 需要系统集成的后端工程师 使用 DDD 的团队 负责跨系统数据一致性的架构师 背景 / 动机 外部系统的字段命名、流程与规则可能与你的领域模型不一致。 如果直接耦合,会让核心领域被污染。 核心概念 反腐败层(ACL):隔离外部模型的适配层 适配与映射:在边界处转换语义 领域模型保护:核心逻辑不被外部侵蚀 实践指南 / 步骤 定义领域模型的核心语义 在边界层做字段与概念映射 把外部协议封装在 ACL 中 为 ACL 设计测试样例 可运行示例 # 外部系统返回的字段命名不同 external_payload = {"user_id": "u-1", "plan": "VIP"} def to_domain(payload): return { "id": payload["user_id"], "membership": "premium" if payload["plan"] == "VIP" else "standard", } if __name__ == "__main__": print(to_domain(external_payload)) 解释与原理 ACL 把外部系统的变化隔离在边界层,避免影响核心业务代码。 它是“保护领域模型”的关键设施。 常见问题与注意事项 ACL 会增加复杂度吗? 会,但能降低长期维护成本。 何时需要 ACL? 当外部系统不受你控制、变化频繁时。 ACL 是否等同于 DTO? 不完全,ACL 是语义转换而非简单结构映射。 最佳实践与建议 ACL 层保持薄而清晰 把外部依赖集中管理 对外部字段变化建立监控 小结 / 结论 反腐败层是系统集成的“防污染墙”。 它让你的领域模型保持清洁与稳定。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

线程安全单例:如何正确实现与验证

副标题 / 摘要 单例常见但容易出错,尤其在并发场景。本文给出线程安全的实现思路与注意事项。 目标读者 使用单例的后端工程师 关注并发正确性的开发者 负责核心基础组件的团队 背景 / 动机 单例常用于配置、连接池或缓存管理。 错误实现会导致多实例或竞态问题。 核心概念 单例:全局唯一实例 线程安全:并发访问不破坏一致性 延迟初始化:按需创建实例 实践指南 / 步骤 优先使用语言内建的单例机制 需要懒加载时使用锁或原子操作 避免在构造函数中做重逻辑 对并发访问写压力测试 可运行示例 import threading class Singleton: _instance = None _lock = threading.Lock() def __new__(cls): if cls._instance is None: with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance if __name__ == "__main__": objs = [] for _ in range(3): objs.append(Singleton()) print(len(set(id(x) for x in objs))) # 1 解释与原理 双重检查锁避免了每次创建都加锁的开销,同时保证并发下只生成一个实例。 关键是“锁内再检查”,避免竞态。 常见问题与注意事项 单例会带来全局状态污染吗? 会,因此应谨慎使用。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

好莱坞原则:别打给我,我会打给你

副标题 / 摘要 好莱坞原则的核心是“框架调用业务代码”,而不是业务代码主动控制流程。本文解释其意义与使用方式。 目标读者 使用框架开发的工程师 设计扩展机制的开发者 关注解耦与控制反转的团队 背景 / 动机 传统流程由业务代码掌控,但当系统需要扩展与统一规范时,框架更适合主导流程。 好莱坞原则就是这种“控制反转”的表述。 核心概念 好莱坞原则:Don’t call us, we’ll call you 控制反转(IoC):流程控制权交给框架 回调/钩子:框架在特定时机调用业务逻辑 实践指南 / 步骤 定义扩展点接口 框架控制主流程 业务只注册回调 通过配置加载插件 保持扩展点稳定 可运行示例 class App: def __init__(self): self.handlers = [] def on_event(self, handler): self.handlers.append(handler) def run(self, event): for h in self.handlers: h(event) def log_handler(evt): print("log:", evt) if __name__ == "__main__": app = App() app.on_event(log_handler) # 注册回调 app.run("start") # 框架调用回调 解释与原理 框架掌控主流程并决定何时调用业务逻辑,这样可以保证统一的生命周期、错误处理与资源管理。 业务只需实现扩展点,减少重复与耦合。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

Active Record vs Data Mapper:差异、优缺点与选型

副标题 / 摘要 Active Record 把数据与持久化绑定在一起,Data Mapper 把持久化隔离为独立层。本文对比二者并给出选型建议。 目标读者 使用 ORM 的后端工程师 设计领域模型的开发者 需要做架构取舍的团队 背景 / 动机 项目变复杂时,持久化模型往往开始“侵入”业务逻辑。 理解 Active Record 与 Data Mapper 的差异,是避免架构污染的关键。 核心概念 Active Record:对象自己保存/加载(数据与持久化耦合) Data Mapper:持久化逻辑在独立映射层 领域模型纯度:业务模型是否被 ORM 污染 实践指南 / 步骤 小型项目可用 Active Record 复杂领域建议 Data Mapper 明确领域边界,避免 ORM 侵入 用 Repository 隔离持久化 测试业务逻辑时替换存储层 可运行示例 # Active Record 风格 class UserAR: def __init__(self, name): self.name = name def save(self): print("save", self.name) # Data Mapper 风格 class User: def __init__(self, name): self.name = name class UserMapper: def save(self, user: User): print("save", user.name) if __name__ == "__main__": UserAR("Alice").save() UserMapper().save(User("Bob")) 解释与原理 Active Record 简单直观,但把持久化耦合进领域模型。 Data Mapper 更复杂,但让业务逻辑更纯粹、更易测试。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

迪米特法则(最少知识原则):违例与修复示例

副标题 / 摘要 迪米特法则强调“只和直接朋友说话”。本文用示例说明违规写法,并给出修复方式。 目标读者 想降低耦合的工程师 负责代码评审与重构的开发者 需要维护大型系统的团队 背景 / 动机 深层链式调用让对象之间依赖过强,改动一个结构就影响一大片。 迪米特法则就是用来控制这种耦合的。 核心概念 最少知识原则:对象只了解直接依赖 消息委托:把内部结构封装在对象内 耦合控制:减少“链式访问” 实践指南 / 步骤 识别链式调用(a.b.c.d) 让中间对象提供必要方法 封装内部结构 避免跨层访问内部字段 可运行示例 class Wallet: def __init__(self, balance): self.balance = balance def has_enough(self, amount): return self.balance >= amount class User: def __init__(self, wallet): self.wallet = wallet def can_pay(self, amount): return self.wallet.has_enough(amount) def checkout(user, amount): # 违例:user.wallet.balance # 修复:user.can_pay return user.can_pay(amount) 解释与原理 通过让 User 暴露 can_pay 方法,调用方无需知道 wallet 的内部结构。 这样 wallet 内部变化时,调用方不需要改动。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

空对象模式的目的:消除空指针分支

副标题 / 摘要 空对象模式用“可用但无效果”的对象替代 null,减少分支判断与空指针风险。本文给出适用场景与示例。 目标读者 频繁处理空指针的工程师 需要简化分支逻辑的开发者 关注代码可读性的团队 背景 / 动机 到处写 if obj is None 会让代码变得难读且易遗漏。 空对象模式通过提供“默认实现”,让调用方无需关心空值。 核心概念 空对象:实现相同接口但执行空操作 统一接口:调用方不区分真实对象与空对象 可替代性:替代 null 而不破坏逻辑 实践指南 / 步骤 定义统一接口 实现真实对象与空对象 在创建阶段选择真实/空对象 调用方不写 null 分支 可运行示例 class Notifier: def send(self, msg: str) -> None: raise NotImplementedError class EmailNotifier(Notifier): def send(self, msg: str) -> None: print("email:", msg) class NullNotifier(Notifier): def send(self, msg: str) -> None: pass def get_notifier(enabled: bool) -> Notifier: return EmailNotifier() if enabled else NullNotifier() if __name__ == "__main__": notifier = get_notifier(False) notifier.send("hello") # 不需要 if 判断 解释与原理 空对象模式把“缺失”变成一个合法对象。 这样调用方无需分支判断,避免空指针错误。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

全局对象为何危险:隐藏依赖与测试失控

副标题 / 摘要 全局对象让依赖变隐式,导致难以测试、难以演进。本文用例子说明其危害,并给出可行替代方案。 目标读者 需要提高可测试性的工程师 经常处理“隐式依赖”的开发者 负责代码质量的团队 背景 / 动机 全局对象看似方便,但会让模块互相耦合,导致“改一个地方牵一大片”。 这在大型系统里是灾难。 核心概念 隐式依赖:调用方不明确传入依赖 共享状态:多个模块写同一对象 测试隔离难:全局状态污染测试 实践指南 / 步骤 识别全局状态 用依赖注入替代 通过参数显式传递依赖 在测试中替换依赖 消除跨模块共享写入 可运行示例 # 反例:全局对象 CONFIG = {"rate": 0.1} def calc(price): return int(price * (1 - CONFIG["rate"])) # 改进:显式注入 def calc_with_config(price, config): return int(price * (1 - config["rate"])) if __name__ == "__main__": print(calc(100)) print(calc_with_config(100, {"rate": 0.2})) 解释与原理 全局对象让依赖隐藏在模块内部,测试时很难替换。 显式传递依赖可以让函数可复用、可测试。 常见问题与注意事项 配置放全局不是很方便吗? 方便但危险,建议在初始化阶段注入。 全局常量也危险吗? 常量问题不大,主要问题在可变全局状态。 如何迁移? 从最核心模块开始逐步消除全局依赖。 最佳实践与建议 用依赖注入替代全局对象 把配置集中在启动入口 尽量避免可变全局状态 小结 / 结论 全局对象是隐藏依赖的温床。 显式依赖是系统可测试与可维护的基础。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

为什么组合优于继承:灵活性、可测试性与演进成本

副标题 / 摘要 继承容易让系统变脆,组合让系统更灵活。本文解释为什么组合更适合工程演进,并给出实用示例。 目标读者 写面向对象代码的工程师 负责模块演进与重构的开发者 做代码评审与架构设计的团队 背景 / 动机 继承会把父类的实现细节暴露给子类,容易导致“脆弱基类问题”。 组合通过“把能力作为对象注入”来降低耦合,更易测试与替换。 核心概念 继承(Inheritance):is-a 关系,强耦合 组合(Composition):has-a 关系,弱耦合 脆弱基类问题:父类改动导致子类行为改变 实践指南 / 步骤 优先建接口,延后继承 把可变行为抽成组件 用组合注入行为 通过依赖替换实现测试 只在“稳定共性”时使用继承 可运行示例 class Logger: def log(self, msg: str) -> None: print(msg) class FileSaver: def save(self, data: str) -> None: print("save", data) class ReportService: def __init__(self, logger: Logger, saver: FileSaver): self.logger = logger self.saver = saver def run(self, data: str) -> None: self.logger.log("start") self.saver.save(data) self.logger.log("done") if __name__ == "__main__": svc = ReportService(Logger(), FileSaver()) svc.run("report") 解释与原理 组合让行为可替换(如 Logger 可以换成 Mock)。 继承则把依赖固定在父类上,一旦父类变化,子类难以控制影响。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]