← 返回题目列表

使用原型模式有哪些常见误区?

高频 困难 第 12 / 25 题 更新于 2026/07/28
原型模式常见误区深拷贝Cloneable过度设计

简化版

原型模式常见误区包括:把 clone() 等同于原型模式、忽略浅拷贝导致共享可变对象、复制了主键和状态字段、用 JSON 或 BeanUtils 盲目深拷贝、简单对象也硬套原型模式。原型模式要围绕业务复制语义设计,而不是机械复制字段。

详细版

常见误区:

  1. 认为原型模式就是 Java Cloneable
  2. 不区分浅拷贝和深拷贝;
  3. 复制可变集合后多个对象互相影响;
  4. 把 ID、订单号、状态、创建时间等字段原样复制;
  5. 用序列化或 JSON 当万能深拷贝;
  6. 复制外部资源对象,如连接、线程、文件句柄;
  7. 原型对象被客户端修改,污染后续副本;
  8. 对象很简单也强行套原型模式;
  9. 对象结构变化后忘记更新复制逻辑;
  10. 没有明确哪些字段共享、哪些字段独立。

使用原型模式前,要先定义清楚复制边界。

完整版教学

一、误区一:把 clone 当成原型模式本身

clone() 只是实现复制的一种方式。

原型模式的核心是:

通过复制已有原型创建新对象

即使用拷贝构造器、copy() 方法、序列化,也可以实现原型模式。

不要把设计模式和某个语言 API 绑定死。

二、误区二:浅拷贝可变引用

这是最常见的问题。

copy.permissions = this.permissions;

这样新旧对象共享同一个权限列表。

如果副本修改权限,原型也被修改。

正确做法通常是:

copy.permissions = new ArrayList<>(this.permissions);

如果列表元素本身也是可变对象,还要继续复制元素。

三、误区三:复制业务唯一字段

很多字段不能原样复制:

  • 数据库主键;
  • 订单号;
  • 用户唯一标识;
  • 创建时间;
  • 支付状态;
  • 审批状态;
  • 运行时缓存。

复制这些字段可能导致重复主键、状态错乱、审计信息错误。

复制方法必须明确哪些字段重新生成。

四、误区四:迷信自动深拷贝

JSON、序列化、BeanUtils 看起来方便,但不会理解业务。

它们不知道:

  • 哪些字段不能复制;
  • 哪些字段应该重置;
  • 哪些对象可以共享;
  • 哪些对象必须深拷贝;
  • 哪些字段需要重新初始化。

自动工具可以辅助,但核心业务复制规则最好显式控制。

五、误区五:让客户端拿到并修改原型

如果原型注册表直接返回原型本体:

DocumentTemplate template = registry.get("contract");
template.setTitle("被污染的标题");

后续复制出来的对象都会受到影响。

更安全的方式是注册表直接返回副本:

DocumentTemplate doc = registry.create("contract");

原型本体应尽量只读或被封装保护。

六、误区六:简单对象也硬套原型

如果对象只有两三个字段:

new User(name, age)

直接构造就很清楚。

原型模式适合复杂对象、模板对象、大量相似对象。简单对象硬套会让代码变绕。

七、常见误区与追问

原型误用最常发生在复制边界不明确。假设用户模板包含 id=101、3个可变标签和1个共享的只读配置,复制后通常应重置 id、深拷贝标签、保留只读配置引用。把所有字段统一浅拷贝或统一序列化深拷贝,都可能破坏业务身份与共享语义。

检查维度判定依据
业务身份字段重置或重新生成
可变子对象按独立性要求复制

心法:先给每个字段标注“共享、复制、重置”,再写复制代码。

  • 误区:深拷贝一定比浅拷贝正确。 不可变配置和共享服务本就应共享,盲目深拷贝会制造错误语义。
  • 追问:怎样发现漏拷贝的可变字段? 修改副本字段并断言原型不变,同时覆盖嵌套集合和元素对象。
  • 误区:复制数据库实体可以保留主键。 把副本当新增实体保存时,原主键可能造成更新旧记录或唯一键冲突。
  • 追问:原型注册表能返回内部原型吗? 不应直接暴露,通常返回副本以防调用者污染后续创建结果。
  • 追问:什么对象不值得用原型? 构造便宜、字段少且没有模板状态的对象,直接构造更清晰。

八、加强记忆

原型模式最怕“复制得太随意”:浅拷贝共享可变对象,深拷贝乱复制业务唯一字段,工具类隐藏复制语义。正确做法是先定义复制边界,再选择 copy()、拷贝构造器或其他实现方式。