重复分支逻辑优先用函数封装,仅在需维护状态、跨调用生命周期或复杂依赖时才考虑对象;避免过早抽象,警惕策略模式滥用,重构应待重复第三次且差异稳定后再进行。

重复分支逻辑用函数封装更直接
多数情况下,重复的业务分支优先用函数(尤其是 static 成员函数或自由函数)封装,而不是立刻建类。函数开销低、意图清晰、测试简单,且不引入不必要的生命周期和状态管理负担。
常见错误是看到“多个相似分支”就本能建 BaseHandler + ConcreteHandler,结果每个子类只重写一个 handle(),还带一堆空构造/析构——这属于过早抽象。
- 分支差异仅在参数或返回值?→ 用函数 + 参数控制(如
process(OrderType type)) - 分支间共享大量中间状态(如缓冲区、上下文标记、重试计数)?→ 才考虑对象封装
- 分支未来大概率要独立配置、替换、监控或注入依赖?→ 对象更合适(但此时通常已超出纯“重复分支”范畴)
对象封装只在状态+行为耦合时成立
如果分支逻辑背后隐含不可忽略的内部状态(比如:需要维护一个 retry_count、缓存上次解析的 schema_version、持有某个 std::mutex),那函数无法自然承载,这时才该提取为对象。
典型信号是:你发现自己在多个函数间反复传入同一组变量(ctx, config, logger),或者函数签名越来越长、参数顺序开始混乱。
- 状态生命周期与调用周期一致?→ 用局部对象(如
JsonValidator validator{config}) - 状态需跨多次调用持续存在?→ 考虑成员变量 + 构造函数注入
- 不同分支共用同一状态但行为隔离?→ 避免把所有分支塞进一个类,按职责拆成多个小对象(如
RetryPolicy、RateLimiter)
警惕“策略模式”滥用导致的虚函数开销
用多态封装分支时,virtual 函数调用本身不是问题,但容易连带引入:动态内存分配(new HandlerImpl)、对象切片、RTTI 依赖、头文件爆炸。尤其在高频路径(如网络包解析、日志格式化)中,这些成本会明显。
替代方案更轻量:
- 用
std::variant+std::visit替代继承层次(C++17,无虚表,编译期分发) - 用函数指针或
std::function存储行为(注意std::function可能触发小对象优化失败) - 模板策略(
template<typename policy></typename>)——零成本,但会增加实例化膨胀
示例:代替 if (type == A) { handlerA.process(); } else if (type == B) { handlerB.process(); },可写为 std::visit([](auto&& h) { h.process(); }, handler_variant);
重构时机比初始选择更重要
第一次写分支时,哪怕预感会重复,也先复制粘贴——等出现第三次重复、且参数/行为差异稳定下来,再提取。过早封装反而阻碍快速验证业务逻辑。
真正容易被忽略的是:分支的“边界”是否真一致。比如看起来都是“处理支付回调”,但支付宝回调含 sign_type 字段校验,微信回调要走证书验签,银联回调要求同步响应 HTTP 状态码——这些差异不是靠一个 PaymentHandler 接口能掩盖的,硬合并会导致条件判断回流到内部,最终变成更难维护的“对象内嵌 if-else”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











