云计算百科
云计算领域专业知识百科平台

【设计模式精讲】1. 为什么 AI 时代的开发者必学设计模式?

【设计模式精讲】为什么 AI 时代必须要懂设计模式?

【摘要】:AI 编程智能体让代码生成前所未有地快,也把结构问题放大得更快。本文从一个 AI 越改越乱的订单函数讲起,拆解分支膨胀、全局状态、继承爆炸、服务耦合和火车链五种坏味道,说明设计模式究竟解决什么、又不解决什么。你将看到:模式的价值不在背类图,而在识别变化、划清职责、建立团队与 AI 都能理解的设计语言;对强调资源生命周期、所有权和多种抽象机制的 C++ 而言,这种能力尤其重要。文章最后也会讨论何时不该使用模式,避免从“不会设计”走向“为模式而模式”。

【关键词】:设计模式、AI 辅助编程、代码坏味道、C++、维护成本

1. AI「写代码」更快了,维护为什么没有更轻松

先讲一个你大概率正在经历的场景。

小张所在的项目组用上了 AI 编程智能体(Agent),产出速度翻了三倍,一个月干完了过去一个季度的活。三个月后需求变更,他让 AI 去改那个 800 行的 processOrder():AI 读完上下文改了三处,测试挂了五处;继续让它修,又挂了三处。提示词越来越长,模型越来越谨慎,结果却没有变得更可靠。

隔壁工位老周的模块开发速度看起来没那么快,但每个函数都小而清楚:订单只维护状态,折扣策略只计算价格,事件分发器只负责通知。AI 接到修改任务后,只需在一个明确边界内工作,一次就通过了测试。

差别不在谁更会写提示词,而在代码有没有设计边界。AI 擅长在给定约束内生成实现,却不会自动知道哪些变化应该被隔离、谁拥有某个资源、一个回调能活多久。没有边界时,模型只能从现有代码猜测意图;而坏结构本身就是最有迷惑性的上下文。

因此,AI 提升的是代码的生成速度,不是结构质量。可以把长期开发效率粗略理解成一个乘法:

交付效率 = 生成速度 × 结构质量 × 验证能力。

其中任何一项接近零,整体都不会好看。设计模式关注的,正是中间那一项。

2. 五种坏味道,背后是同一个问题

先看一段完整的最小示例:

enum class OrderType { Normal, Vip, Promo, Flash };

// 每加一种订单,这个函数就要继续增长
double calcPrice(OrderType t, double raw) {
if (t == OrderType::Normal) return raw;
if (t == OrderType::Vip) return raw * 0.9;
if (t == OrderType::Promo)
return raw >= 100.0 ? raw 20.0 : raw;
if (t == OrderType::Flash) return raw * 0.7;
return raw;
}

它可以工作,问题也不在 if 本身。真正的风险是:订单类型与计算规则会一起变化,每加一种规则都必须打开这个函数,已有分支也跟着进入回归范围。第 19 篇策略模式会回来重写它,把“选择算法”和“执行算法”拆开。先记住这个函数,它是全专栏的反面教材 1 号。

坏味道很少单独出现。下面四段是为突出结构问题而省略类型定义的说明性片段,不是可独立编译单元。

反面教材 2 号:全局配置,谁都能改

Config::instance().set("timeout", 3);

问题不只是“用了单例”,而是任何代码都能在任何时刻修改共享状态。出了故障,没人说得清谁改过它;并行测试还可能互相污染。全局唯一不等于全局可写,第 4 篇单例模式会专门讲清这条边界。

反面教材 3 号:继承叠罗汉

class Logger {};
class TimedLogger : public Logger {};
class EncryptedTimedLogger : public TimedLogger {};
class CompressedEncryptedTimedLogger
: public EncryptedTimedLogger {};

三个可以独立开关的功能,理论上有八种组合。如果每种组合都声明一个子类,类图很快就会变成排列组合表。问题在于把“可以组合的能力”焊死成了继承层级。第 12 篇装饰器模式会用对象包装替换这座类塔。

反面教材 4 号:订单服务认识半个系统

void OrderService::pay() {
inventory_.deduct(order_);
points_.add(order_);
push_.notify(order_);
}

在这种直接依赖的实现中,每增加一个订阅方,都要修改订单模块并重新验证原有流程。订单既处理支付,又负责枚举所有后续动作,变化来源不断增加。第 18 篇观察者模式会让订单只发布事实,由订阅者决定如何响应。

反面教材 5 号:一路挖穿内部结构

std::string city =
order.warehouse().address().city();

问题不在“写了三个点”,而在调用方被迫知道订单包含仓库、仓库包含地址这套内部导航结构。只要其中一层改变,外部代码就会被连带修改。第 2 篇会用迪米特法则分析它,第 11 篇则会展示如何用外观提供更稳定的入口。

这五种坏味道的共同点是:局部代码可能完全正确,变化却无法停在局部。 设计模式不是负责修正语法,而是帮助我们把不同变化放进不同边界。

3. 设计模式是什么,又不是什么

一句话定义:设计模式是针对反复出现的设计问题,经过实践总结的一类可复用解决思路。 它通常会描述问题发生的上下文、参与角色、协作关系、适用条件以及代价,而不只是给出一段代码。

“模式”这一思想源自建筑学家 Christopher Alexander。软件领域在 GoF 之前已经出现了相关探索;1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著的《设计模式:可复用面向对象软件的基础》系统整理并推广了 23 种经典模式。它们后来成为面向对象设计中的一套共同词汇,也是本专栏的核心内容。

理解模式,还要先划清四条边界:

  • 模式不是算法:算法主要描述计算步骤,模式主要描述对象职责与协作结构。
  • 模式不是框架:框架是可以直接依赖的具体软件,模式可以用不同语言和库实现。
  • 模式不是代码模板:同一个观察者模式可以用继承、std::function、信号槽或消息队列表达。
  • 模式不是质量保证:选对名字但忽略线程、所有权和异常安全,代码仍然可能出错。

所以,学模式不是收集 23 段标准答案,而是建立“看到某种变化,就能联想到几种成熟组织方式”的反应能力。

4. 学习模式能得到什么

第一是复用经验。你遇到的“对象如何创建”“算法如何替换”“状态变化后如何通知”并不新鲜。模式把前人踩过的坑、方案的收益和代价一起打包,让你不必每次从零发明结构。

第二是建立共同语言。评审时说“这里需要把折扣提成策略”,团队会立刻讨论策略由谁创建、如何注入、是否需要运行时切换。没有这套词汇,大家可能要用很长一段自然语言,才能描述同一个协作关系。模式名不是炫技术语,而是一种经过压缩的设计说明。

第三是控制变化的传播范围。工厂把创建变化集中起来,装饰器把可叠加功能拆开,观察者把事件发布者与响应者分离。它们并不会消灭变化,但能让变化尽量停在预期位置。

模式之于程序员,有点像棋谱之于棋手。没人仅靠背棋谱赢棋,但熟悉典型局面的人能更快看见风险和候选方案。模式给你的不是唯一答案,而是识别问题和比较取舍的坐标系。

5. 为什么 AI 时代反而更需要设计能力

生成变便宜以后,判断更值钱。 AI 可以迅速给出继承版、模板版和函数对象版实现,但选哪一种、放在哪一层、半年后怎样扩展,仍然需要结合业务变化、性能、ABI、团队能力和生命周期约束做判断。模型可以列出选项,责任不会因此转移。

评审 AI 代码需要准确词汇。 例如,与其说“通知代码感觉不安全”,不如指出:“这里在遍历观察者容器时允许回调注销自身;若实现原地擦除并使当前迭代器失效,后续遍历可能产生未定义行为,应考虑快照遍历、延迟删除或稳定句柄。”这条意见能直接转化为修改和测试。

提示词也需要设计语言。 “把代码改灵活一点”没有说明变化方向;“把折扣计算提成策略,通过构造函数注入,订单只依赖抽象,并明确策略与订单的生命周期关系”则给出了职责、依赖和所有权。AI 得到的不是一个模式名字,而是一组可验证的设计约束。

一套更可靠的协作流程是:

  • 人先说明业务不变量、变化方向和所有权。
  • AI 在边界内生成候选实现与测试。
  • 人用设计原则检查耦合、生命周期和异常路径。
  • 编译器、静态分析与测试验证最终行为。
  • 设计模式在这里扮演的是沟通协议:让人、AI 和代码评审围绕同一套结构概念工作。

    6. 为什么 C++ 开发者尤其需要

    不同语言和生态对应用结构的约束程度不同:

    生态常见的结构支持开发者仍需决定什么
    Java / Spring 依赖注入、事件机制、代理等约定较普遍 模块边界、扩展点与业务模型
    Python 协议、装饰器、迭代器等表达简洁 动态约束、模块职责与运行时边界
    C++ 提供多种语言机制和标准库组件 抽象方式、所有权、生命周期与应用架构

    具体到 C++,虚函数和模板属于语言特性,智能指针、容器和 std::function 属于标准库组件。它们提供的是零件,不会替你决定订单系统如何分层、事件由谁拥有、回调何时失效。同一个问题既能用运行时多态解决,也能用模板、类型擦除或普通函数组合解决,选择空间很大,代价模型也各不相同。

    现代 C++ 倡导使用 RAII 管理资源,并不意味着所有风险都会自动消失。共享所有权可能形成环,裸引用可能悬空,回调可能捕获已经销毁的对象,错误的模板边界还可能放大编译时间。设计模式与 C++ 的所有权工具结合后,才能把“谁依赖谁”和“谁负责释放谁”同时说清楚。

    因此,C++ 开发者真正需要的不是手写某种固定模式,而是识别模式意图,并选择符合当前性能、生命周期和部署约束的实现方式。

    7. 什么时候不该使用设计模式

    模式有成本:更多类型、更多间接调用、更长的调用链,也可能增加调试和理解难度。只有一个稳定实现时,提前建立抽象工厂通常没有收益;两行清楚的条件判断,也不必为了“消灭 if”塞进策略体系。

    决定是否引入模式前,可以问三个问题:

  • 这里是否存在已经发生,或有明确证据会发生的独立变化?
  • 当前结构是否让一次变化扩散到多个不相关模块?
  • 新抽象带来的维护成本,是否小于它隔离变化的收益?
  • 如果答案并不明确,先保留简单实现和可靠测试。模式应该在问题成熟时出现,而不是在需求尚未发生时抢先占位。原则是方向,不是法律;能解释“不使用某个模式”的理由,也是一种设计能力。

    8. 这个专栏怎么读

    • 先原则后模式:第 2 篇先讲 SOLID、合成复用和迪米特法则,建立判断标准。
    • 先会读图再写代码:第 3 篇介绍类图、序列图和状态机图,后文用图表达结构与协作。
    • 沿三类主线推进:创建型关注对象怎么造,结构型关注对象怎么组装,行为型关注对象怎么协作。
    • 用对比和实践收口:后续 27 篇包含 23 种 GoF 模式、三篇分类小结和一篇综合实战。
    • 区分片段与完整示例:开篇短片段用于说明问题;后续模式篇的完整示例统一按 C++17 做语法检查。

    本篇小结

    • AI 提高代码生成速度,但职责边界、所有权和架构取舍仍需人来定义。
    • 设计模式提供成熟经验、共同语言和控制变化传播范围的方法,但不是万能模板。
    • C++ 的抽象与资源模型选择丰富,更需要把协作关系和生命周期一起设计。
    • 真正掌握模式,也包括知道何时保持简单、暂时不用模式。
    • 下一篇将进入判断结构好坏的基础:SOLID 与几条关键设计原则。
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【设计模式精讲】1. 为什么 AI 时代的开发者必学设计模式?
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!