原型模式
一个价值 50 万的复制粘贴 bug
2022年,我们团队接到了一个线上告警:订单系统的优惠券数据全部错乱。几百个用户收到了错误的优惠券金额。
排查了 4 个小时,发现根因是一位同学在复制优惠券模板时,直接用了这个操作:
结果是 100 个"优惠券"其实都是同一个对象,修改任何一个,所有优惠券都会变。
这就是原型模式要解决的核心问题:如何正确地复制一个对象,而不是复制引用。
一、核心思想
原型模式:用原型实例指定创建对象的种类,并且通过复制原型来创建新的对象。
二、Cloneable 方案🔴
2.1 基础用法
Java 提供了一个 Cloneable 标记接口来实现原型模式:
2.2 浅拷贝的问题
Object.clone() 是浅拷贝——只复制对象的基本类型字段和引用字段的地址,不复制引用指向的对象。
【架构权衡】 浅拷贝适用于:对象结构简单、没有嵌套引用、或者嵌套对象不需要独立复制的场景。深拷贝适用于:对象结构复杂、嵌套对象需要独立修改、或者出于线程安全的考虑需要完全隔离的场景。
三、深拷贝方案🟡
3.1 方案一:递归手动拷贝
3.2 方案二:序列化拷贝
⚠️
序列化拷贝有两个严重问题:
- 性能开销大:需要序列化所有字段,复杂对象可能很慢
- 需要实现 Serializable:每个嵌套对象都要实现
- transient 字段会被跳过:需要深度拷贝的字段不能加 transient
对于性能敏感的场景(如高频交易、游戏服务器),序列化拷贝是不可接受的。
3.3 方案三:JSON 序列化拷贝
JSON 拷贝的好处:
- 不需要类实现 Serializable
- 对嵌套对象的拷贝是全自动的
- 可以控制忽略哪些字段(Jackson 注解)
坏处:
- 性能最慢
- 精度问题(浮点数、日期格式)
- 不能拷贝非 JSON 兼容的对象
3.4 深拷贝方案对比
四、原型管理器
当原型对象较多时,需要一个管理器来统一管理:
使用方式:
五、生产应用场景
5.1 文档模板系统
5.2 游戏中的怪物/角色复制
【面试官心理】 原型模式在面试中属于"看起来简单但细节很多"的类型。能写出浅拷贝/深拷贝区别的有 60%,能说出各种深拷贝方案优缺点的有 30%,能结合自己项目场景说清楚选型原因的只有 10%。原型模式和其他创建型模式的区别,也是高频追问。
六、与工厂模式的对比
💡
原型模式和工厂模式不是互斥的。可以把原型模式理解为"工厂模式的变体"——工厂模式通过类型创建对象,原型模式通过复制创建对象。在实际系统中,两者经常共存。
七、工程代价评估
八、面试总结
8.1 核心追问
- "浅拷贝和深拷贝的区别是什么?" —— 基本题,必须答对
- "Object.clone() 为什么是浅拷贝?" —— 要说出内存模型和引用复制的原理
- "如何实现深拷贝?" —— 至少说出两种方案及各自的优缺点
- "什么场景下会选择原型模式而不是工厂模式?" —— 区分度高的题