恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

设计模式怎么落地:单例、工厂、策略这三个最容易被用错的地方

  • 首页
  • 资讯中心
  • /
  • 设计模式怎么落地:单例、工厂、策略这三个最容易被用错的地方

相关资讯

高防IP实战:大流量DDoS攻击的清洗策略与阈值调优 2026/10/11 16:33:04
分步傅里叶法解非线性薛定谔方程:光纤脉冲传播仿真源码详解 2026/10/11 16:33:04
通快TRUTOPS安装配置全指南:路径、服务、许可证三大核心要点 2026/10/11 16:33:04

最新资讯

deepin 运行 Windows 应用:兼容层选型与体验优化指南
HTML5多图片上传预览:从FileReader到Canvas压缩的完整指南
zhengxi-views的7种玩法:从“郑希怎么看光通信“到给基金打分,一次问对的完整清单
论文骨架一眼看清:zotero-AI-Butler思维导图自动生成与PNG/OPML导出指南
工业机器视觉缺陷检测:硬件选型与成像测试全流程解析
pytorch-openpose实战:姿态估计与手部关键点检测全解析

今日推荐

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

设计模式怎么落地:单例、工厂、策略这三个最容易被用错的地方

发布时间:2026/10/11 16:33:04
设计模式怎么落地:单例、工厂、策略这三个最容易被用错的地方 本文首发于 CSDN转载请注明出处。先说结论23 个设计模式里日常真正高频用到的只有五六个。而单例、工厂、策略这三个恰好是最常用、也最容易被用错的——单例错在少写了volatile工厂错在把简单工厂当成了 GoF 模式策略错在把它当成了消灭 if-else的万能药。这篇把三个模式的适用边界和实现细节讲清楚。23 个模式真正高频的就那几个先给一个坐标。GoF 原书把模式分成三类共 23 个分类数量模式创建型5抽象工厂、建造者、工厂方法、原型、单例结构型7适配器、桥接、组合、装饰器、外观、享元、代理行为型11责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者日常代码里出现频率最高的是哪几个单例配置、连接池、工厂/建造者对象构造复杂时、策略多分支逻辑、代理AOP 底层、观察者事件、模板方法框架回调。其余的多数是知道它存在、看到能认出来就够。这一点值得先摆正因为最容易出问题的不是不懂模式而是把所有模式都当成了必须套用的模板。单例的五种写法为什么只有两种值得用单例的目标很简单保证一个类只有一个实例并提供全局访问点。但只有一个实例在多线程下就不简单了。看代码// 有问题版本双重检查锁少写了 volatilepublicclassConfig{privatestaticConfiginstance;publicstaticConfiggetInstance(){if(instancenull){synchronized(Config.class){if(instancenull){instancenewConfig();// 问题在这里}}}returninstance;}}这段代码看起来没问题但它不可靠。JSR-133 内存模型的核心作者 Bill Pugh 等人在那篇著名的《Double-Checked Locking is Broken》里说得很清楚the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object.翻译构造对象的写操作和把引用赋给字段的写操作可能被重排于是另一个线程可能看到一个非 null 但还没构造完的对象——字段全是默认值。这种 bug 极难复现一旦出现又很难定位。修法就是加volatile。依据在 Java 语言规范里§17.4.5 的 happens-before 规则明确写着对 volatile 字段的写 happens-before 其后对该字段的读。这一条保证了构造过程对读线程可见。五种写法的取舍写法线程安全延迟加载防反射/序列化破坏推荐度饿汉式安全否否简单场景可用懒汉式加锁安全是否性能差不推荐双重检查锁 volatile安全是否可用静态内部类安全是否推荐枚举安全否是推荐静态内部类的线程安全来自 JLS 的类初始化保证——类初始化由 JVM 加锁、只执行一次而且是在首次真正用到时才触发天然兼顾了延迟加载和线程安全还不用写同步代码。枚举更强。《Effective Java》Item 3 的原文评价是This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.“ironclad guarantee”铁一般的保证不是夸张。JLS §8.9 规定“An enum class has no instances other than those defined by its enum constants.”——构造器不能从外部调用反射也受阻序列化机制有特殊处理不会产生第二个实例。这就是为什么 Item 3 的结论是单元素枚举是实现单例的最佳方式。单例就是全局变量吗不是虽然 GoF 原书里单例的 Intent 确实包含 “provide a global point of access to it”。两者的区别在于单例还负责控制实例化的范围——它保证唯一性、可以延迟初始化、可以实现接口被替换测试时换成 mock。全局变量什么约束都没有。写代码时这个区别很实际如果你的单例只是想找个地方放全局状态那它确实退化成了全局变量而且比全局变量更难测试没法替换。判断标准是——这个类有没有真的需要唯一实例这个约束。没有的话用依赖注入传进去更干净。简单工厂为什么不算 GoF 模式工厂这块最容易踩的概念坑在这里。GoF 原书里跟工厂有关的是两个模式工厂方法Factory Method和抽象工厂Abstract Factory。平时写得最多的那种一个静态方法里 switch 一下返回不同子类官方名字叫简单工厂它不在 GoF 的 23 个模式里。《Head First Design Patterns》里对它有个很准确的定性p.119Just because Simple Factory isn’t a REAL pattern doesn’t mean we shouldn’t check out how it’s put together.它是编程习惯programming idiom不是模式。这三个的区别名称是不是 GoF 模式结构适用场景简单工厂否一个工厂类 switch/if产品种类少且稳定工厂方法是每个产品一个工厂子类产品种类会扩展抽象工厂是一个工厂接口创建一族产品需要保证产品族配套这不是咬文嚼字。简单工厂的问题是加一个新产品就要改工厂类违反开闭原则工厂方法把创建哪个产品推迟到子类扩展时只加类不改老代码。选哪个取决于你的产品种类会不会经常增加——会就上工厂方法。顺带一句Spring 的BeanFactory名字里带 Factory但它本质是个容器跟 GoF 的工厂方法模式不是一回事。策略模式只是为了消灭 if-else 吗策略模式的官方 IntentGoF 原书Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.它解决的问题是算法变体需要在运行时切换且客户代码不应该知道具体变体的实现。GoF 在 Applicability 里确实列了一条类里有一堆行为表现为多个条件语句时可以把条件分支挪进独立的策略类。这条被广泛引用为策略模式就是用来消灭 if-else 的但顺序常被搞反不是看到 if-else 就该上策略而是这些分支代表的是可互换的算法族且未来还会增加才值得上策略。如果分支只有两三个、永远不变硬套策略模式的结果是多出五六个类、一个注册表、一个选择逻辑——代码总量翻了倍可读性还降了。这叫过度设计。写一个最小的策略模式// 策略接口publicinterfaceDiscountStrategy{BigDecimalapply(BigDecimalprice);}// 具体策略publicclassVipDiscountimplementsDiscountStrategy{OverridepublicBigDecimalapply(BigDecimalprice){returnprice.multiply(newBigDecimal(0.8));}}// 上下文持有策略不关心具体实现publicclassPriceCalculator{privatefinalDiscountStrategystrategy;publicPriceCalculator(DiscountStrategystrategy){this.strategystrategy;}publicBigDecimalcalc(BigDecimalprice){returnstrategy.apply(price);}}注意上下文只依赖接口。这样新增一个折扣类型时老代码一行都不用改。策略和状态长得一样区别在哪这两个模式在 UML 上几乎长得一样是面试和实战里都容易混的一对。区别在意图策略「定义一族可互换的算法让算法独立于客户变化」——客户主动选一个策略状态「对象内部状态改变时改变其行为看起来像换了个类」——状态自己会流转一个简单的判据属于工程经验不是原书规定策略之间互相不知道彼此由外部注入状态之间可以触发转换由对象自己管理。你在写支付方式选择——那是策略在写订单从待支付到已支付到已发货——那是状态机。Spring 的 singleton 是单例模式吗这条被误解得最广。Spring 官方文档明确否认了等价关系Spring’s concept of a singleton bean differs from the singleton pattern as defined in the Gang of Four (GoF) patterns book. The GoF singleton hard-codes the scope of an object such that one and only one instance of a particular class is created per ClassLoader. The scope of the Spring singleton is best described as being per-container and per-bean.关键差别是作用范围GoF 单例Spring singleton唯一范围每个 ClassLoader 一个每个容器一个如何保证类内部硬编码私有构造 静态方法容器负责管理能否有多个实例不能能多个容器就有多个所以一个 Spring 应用里同一个类完全可能有两个实例父容器和子容器各一个。写代码时如果依赖全进程唯一用 Spring 的 singleton 是错的——它只在单容器内成立。顺便Spring 官方对 prototype scope 的描述也常被误读它是每次请求都创建新实例容器不负责销毁这些 bean 的完整生命周期销毁回调不会自动调用这点在需要释放资源时要自己管。三个流传说法对一下“用了设计模式代码就一定更好”——原书从没这么说。GoF 给每个模式都单列了 “Consequences”后果一节讲的就是引入这个模式的代价。模式是用来描述问题的词汇不是必须达成的目标。“策略模式必须配合工厂”——没有这个要求。GoF 里策略模式的参与者只有 Context、Strategy、ConcreteStrategy 三个不含工厂。谁来选策略由你决定可以客户端传可以 Spring 注入也可以配个工厂——但工厂从来不是必需的。“单例模式就是全局变量”——近似但不准确。单例多了一层唯一性约束和实例化控制这层约束才是它存在的理由。丢掉这层约束它确实就成了全局变量。设计模式的适用边界最容易随着项目演进而失真笔记里写死的结论过两年就不对了。我现在的做法是按「模式—典型场景—反面案例」整理成对照表统一维护需要的时候用AI 图文同步一次推到几个平台留档集中复盘的那几天靠发文额度提升不用排队发完再用批量 GEO 检测确认这些内容在 AI 搜索里有没有被引用墨衍会员权益 有需要可以了解。常见问题Q枚举单例有什么缺点最大的问题是不够灵活不能继承别的类枚举已经继承了Enum也没有真正的延迟加载枚举常量在类初始化时就全部创建。如果单例对象初始化很重、或者启动时不想创建静态内部类更合适。另外枚举单例在依赖注入框架里用起来也不自然。Q什么情况下不该用单例需要被替换以方便测试时、需要多个独立实例时、依赖外部资源数据库连接、HTTP 客户端时。这几种情况更适合依赖注入 容器管理生命周期——Spring 的 singleton bean 恰好是这种形态而不是 GoF 单例。Q策略模式里策略多了怎么选常见三种做法客户端显式传入最简单、可测、用 Map 注册 按 key 取避免 if-else、用 Spring 收集所有实现类MapString, Strategy注入key 是 bean 名。无论哪种选择逻辑都应该集中在一处散落各处就白拆了。Q工厂方法模式是不是过度设计了看产品是否可扩展。如果只有两个产品且基本不会再增用简单工厂甚至直接new就行这也是很多团队的实际做法。判断标准是新加一个产品时需要改动的文件数是 1 还是 N。如果是 N说明缺少扩展点。Q怎么判断一个地方该不该上设计模式一个实用的顺序先让代码能跑通、能被读懂等发现同一处逻辑被反复修改、或者同一个判断散落多处时再回过头看哪个模式能收敛它。反过来先套模式几乎一定得到一堆只有作者本人能看懂的抽象层。关于墨衍如果你也在多个平台发技术文章值得看看墨衍。三个最常用的权益——发文额度提升密集更新不再受限、批量 GEO 检测一次扫完全部文章的 AI 引用状态、AI 图文同步一稿多平台分发。点这里了解墨衍会员

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号