开头
上次在内部技术分享的时候,我让团队里几个刚工作一两年的同学说说自己用过哪些设计模式。结果很有意思,大家脱口而出的基本上都是单例、工厂、策略这三个,再往后就要想很久。面试的时候人人能背出二十三(GoF)种模式的名字,可真到了写代码的时候,能主动用上的其实很少。
这个现象我特别理解。我刚工作那会儿也是这样,觉得设计模式是一个个孤立的"标准答案",学一个记一个,用的时候却对不上号。后来代码写多了,踩的坑多了,再回头看才发现:设计模式不是背出来的,是"长"出来的——当一个变化反复出现,当一个需求改起来越来越痛,你就会自然想到某种模式。它本质上是一套应对"变化"的成熟思路,是前人把"怎么改代码才不痛苦"这个问题的答案沉淀了下来。
这篇文章把我这些年工作中真正高频用到的模式做一个梳理。不追求面面俱到把二十三种都讲一遍,而是挑那些在真实项目里出现频率最高、解决痛点最直接的模式,讲清楚它们解决的问题、适用场景、常见误用,以及在新兴的AI编程(尤其是多Agent协作)场景下,这些经典模式又是怎么变形的。无论你是在准备面试、写课程作业,还是想在真实项目里把代码写得更好维护,这篇应该都能给你一些参考。
1. 先回答一个问题:设计模式到底解决的是什么
很多人学设计模式会陷入一个误区——把模式当成"高级技巧",觉得用了设计模式的代码才是好代码。这个认知偏差非常普遍,也导致了很多项目里出现"为了模式而模式"的过度设计。
1.1 找变化点,封装变化
设计模式解决的核心问题,用一句话概括就是:找到变化点,封装变化。它不是为了让代码看起来更复杂或更"高级",而是为了让未来改动时影响范围最小、成本最低。
举个最简单的例子。你写了一个订单金额计算的方法,一开始只有普通订单,代码特别直白:
java复制public double calculate(Order order) {
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getCount();
}
return total;
}
后来需求变了,会员打九折。你加了一行:
java复制if (order.isMember()) {
total *= 0.9;
}
再后来,节假日满减、VIP折上折、新用户首单特惠……这个calculate方法越来越长,if判断越堆越多,每次改促销规则都要动这个方法,改完还得担心影响其他逻辑。这时候你就触碰到了"变化点"——促销规则在变。而策略模式就是专门解决这类问题的:把变化的算法抽出来,让主流程不感知具体规则。
这就是设计模式价值最直接的体现:它让你的代码在频繁变化的需求面前,依然能保持稳定。谁在变,就把谁封装起来,然后通过组合、委托、接口来桥接稳定和变化。
1.2 模式的三个层次:思想、原则、招式
我把设计模式的学习分成三个层次,这个分层帮我自己理清了很多困惑:
| 层次 | 内容 | 作用 |
|---|---|---|
| 思想层 | 封装变化、面向接口编程、组合优于继承 | 指导性最强,是"道" |
| 原则层 | 单一职责、开闭原则、最少知识原则、依赖倒置等SOLID原则 | 连接"道"与"术"的桥梁 |
| 招式层 | 二十三(GoF)种设计模式 | 具体的代码结构,是"术" |
很多初学者一上来就背模式本身(招式层),但缺少思想和原则的支撑。这就导致一个问题:知道策略模式是什么样子,但面对一个具体业务场景时判断不出来该不该用。掌握设计模式的核心路径,是先理解"面向对象设计原则",然后在大量真实代码里反复感受"变化在哪里",最后模式自然就会浮现出来。
比如开闭原则(对扩展开放,对修改关闭)——理解了这条原则,你再看装饰器模式、策略模式、观察者模式,会发现它们都是"不修改已有代码就能增加新能力"的具体实现手段。思想通了,招式就活了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 创建型模式:最常被用错也最常被需要的三个
创建型模式解决的问题是"怎么创建对象"。这一块很多人觉得简单——不就是new一个对象吗?但实际项目里,"创建对象"这件事的复杂度经常被严重低估。
2.1 单例模式:不是用来"省内存"的
单例模式大概是争议最大的一个模式。很多人觉得它就是个"全局变量",是不好的设计。但我要说:单例本身不可怕,可怕的是把什么东西都做成单例。
单例真正的使用场景是"共享状态"和"全局唯一",而不是"省内存"。一个配置文件加载器、一个数据库连接池、一个线程池,这些对象在进程里确实只需要一份,而且需要被多处共享访问,这时候单例是合理的。
我见过最典型的使用误区,是有人拿单例来"减少对象创建的开销"。比如创建一个查询用户信息的Service,明明是无状态的逻辑对象,非要写成单例。这完全没有必要——无状态对象创建成本极低,GC完全能处理,用单例反而带来了全局共享的隐患。真正值得单例的,是有状态的、创建成本高的、需要线程安全的资源对象。
实现方式上,我推荐饿汉式或者静态内部类式,日常项目不要用双重检查锁。原因很简单:双重检查锁要考虑volatile、要考虑性能,写错概率高,收益却几乎为零。Java里枚举单例是Effective Java里推荐的写法,但实际项目中我见到的还是静态内部类最多:
java复制public class ConfigManager {
private ConfigManager() {
// 加载配置
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
}
这种写法好处是:延迟加载、线程安全、代码简单。而且构造方法私有化,杜绝了外部new的可能。
2.2 工厂系列:把"创建逻辑"从业务里剥出去
工厂模式是最容易被低估的模式。很多人觉得工厂不就一个if-else返回不同对象吗,有什么好讲的?但恰恰是这个if-else,放对了位置能让代码结构无比清晰,放错了就是灾难。
工厂有三种形态,我工作中用到最多的是简单工厂和工厂方法,抽象工厂相对少一些。
简单工厂适合产品种类不那么多、创建逻辑比较
