闭包和类的共同点:为什么它们都能“携带状态”

副标题 / 摘要 闭包和类都能“携带状态”,只是表达方式不同。本文用简单示例对比二者的共性与差异。 目标读者 学习函数式与面向对象的开发者 想理解“状态封装”概念的人 需要在不同范式间切换的工程师 背景 / 动机 闭包常被认为是函数式特性,类是面向对象特性。 实际上它们都解决“状态 + 行为绑定”的问题。 核心概念 闭包:函数捕获外部变量形成状态 类/对象:属性 + 方法封装状态 封装:隐藏内部状态细节 实践指南 / 步骤 用闭包实现一个计数器 用类实现同样功能 对比可读性与扩展性 选择更适合的表达方式 可运行示例 # 闭包实现 def make_counter(): x = 0 def inc(): nonlocal x x += 1 return x return inc # 类实现 class Counter: def __init__(self): self.x = 0 def inc(self): self.x += 1 return self.x if __name__ == "__main__": c1 = make_counter() print(c1()) c2 = Counter() print(c2.inc()) 解释与原理 闭包通过捕获变量保存状态,类通过属性保存状态。 二者都把“状态 + 行为”绑定在一起。 ...

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

用多态替换 if:把流程判断变成对象职责

副标题 / 摘要 大量 if 判断会让代码难以维护。本文通过职责拆分与多态消除条件分支。 目标读者 需要重构遗留代码的工程师 关注可维护性的团队 学习设计原则的开发者 背景 / 动机 if 分支常常是“规则塞在一起”的信号。 当规则变化时,分支会持续膨胀。 核心概念 职责拆分:让对象承担自己的规则 多态:用对象替代条件判断 空对象:避免 null 判断 实践指南 / 步骤 识别 if 判断的业务规则 为规则创建对象或策略 用空对象替代 null 分支 把规则拆成可测试单元 可运行示例 class Foo: def do(self, file): return f"process {file}" class NullFoo(Foo): def do(self, file): return "" def get_foo(repo, key): return repo.get(key, NullFoo()) if __name__ == "__main__": repo = {"a.xml": Foo()} foo = get_foo(repo, "a.xml") print(foo.do("a.xml")) 解释与原理 把“有/无对象”的判断交给对象本身(Null Object), 可减少 if 分支并提升可读性。 ...

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

用多态替换 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]

多继承 vs 多接口:对“正交性”的影响

副标题 / 摘要 多继承能直接复用实现,但也容易破坏正交性;多接口更强调行为组合。本文对比两者的工程影响。 目标读者 使用面向对象语言的开发者 关注可维护性与复杂度的工程师 需要设计可组合 API 的团队 背景 / 动机 多继承能快速复用代码,但容易引发菱形继承等复杂问题。 多接口更安全,但需要通过组合实现行为。 核心概念 多继承:继承多个实现 多接口:继承多个行为契约 正交性:特性可以独立组合而不相互干扰 实践指南 / 步骤 优先用接口表达能力 复用实现时优先组合而非继承 避免菱形继承与复杂层级 用测试保证组合行为正确 可运行示例 interface Loggable { void log(String msg); } interface Auditable { void audit(String msg); } class Service implements Loggable, Auditable { public void log(String msg) { System.out.println("log:" + msg); } public void audit(String msg) { System.out.println("audit:" + msg); } public static void main(String[] args) { Service s = new Service(); s.log("hello"); s.audit("hello"); } } 解释与原理 多接口强调能力组合,避免继承链带来的隐式耦合。 多继承虽然更直接,但容易破坏正交性并增加维护成本。 ...

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]