name: Prototype description: "通过克隆现有实例来创建对象" license: MIT
Prototype Pattern (原型模式)
核心概念
Prototype 是一种创建型设计模式,通过克隆现有实例(原型)来创建新对象,而不是通过调用构造函数。
核心原理
- 🔄 复制而非构造 - 复制一个已存在的对象
- ⚡ 性能优化 - 避免复杂的初始化
- 🔀 状态保留 - 新对象继承原型的状态
- 📦 运行时灵活 - 在运行时选择要克隆的对象
关键问题:浅拷贝 vs 深拷贝
这是Prototype模式最关键的问题!
// 浅拷贝(Shallow Copy) - 只复制引用
public class ShallowCopy {
public User clone() {
User copy = new User();
copy.id = this.id; // ✅ 基本类型复制
copy.name = this.name; // ✅ String复制(特殊)
copy.address = this.address; // ❌ 对象引用复制 → 共享!
return copy;
}
}
// 问题示例
User original = createUser();
User copy = original.clone();
copy.getAddress().setCity("Beijing"); // ❌ 修改了原对象的Address!
assert original.getAddress().getCity().equals("Beijing"); // 原对象也变了!
// 深拷贝(Deep Copy) - 递归复制所有对象
public class DeepCopy {
public User deepClone() {
User copy = new User();
copy.id = this.id;
copy.name = this.name;
copy.address = this.address.deepClone(); // ✅ 递归复制
return copy;
}
}
// 正确
User original = createUser();
User copy = original.deepClone();
copy.getAddress().setCity("Beijing"); // ✅ 只影响副本
assert original.getAddress().getCity().equals("Shanghai"); // 原对象不变!
何时使用
始终使用:
- 复制成本远低于创建成本 - 对象初始化复杂且耗时
- 数据库连接、配置对象、大图片...
- 需要保留对象完整状态 - 包括内部细节
- 数据库记录、缓存快照、编辑历史...
- 对象创建过程不透明 - 第三方库对象
- 需要独立对象副本 - 避免状态污染
触发短语:
- "如何快速创建相同配置的对象?"
- "需要对象的完整备份"
- "对象初始化很复杂"
- "数据库记录克隆"
- "UI组件模板复制"
- "缓存快照"
- "撤销/重做"
真实场景检查:
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 创建用户对象 | ⚠️ 否 | 创建成本低,直接new即可 |
| 克隆数据库记录 | ✅ 是 | 包含许多字段和关系,克隆更简单 |
| 复制配置对象 | ✅ 是 | 配置复杂,拷贝比重新配置快 |
| 实现撤销功能 | ✅ 是 | 需要保存完整状态 |
| 游戏保存系统 | ✅ 是 | 游戏状态复杂,深拷贝很要紧 |
基本结构
UML类图
┌─────────────┐
│ Prototype │
│─────────────│
│ +clone() │
└──────┬──────┘
△
│
┌──────┴─────────┐
│ │
┌────┴────┐ ┌─────┴───┐
│Concrete1│ │Concrete2│
│+clone() │ │+clone() │
└─────────┘ └─────────┘
Client
↓
prototype.clone() → 快速创建副本
参与者
- Prototype - 定义clone方法
- ConcretePrototype - 实现clone方法
- Client - 调用clone()创建新对象
- PrototypeRegistry - (可选) 管理原型实例
4种实现方法
方法1: Java Cloneable接口 (浅拷贝)
// ✅ 方法1: 最简单,但注意浅拷贝问题
public class User implements Cloneable {
private int id;
private String name;
private Address address; // 复杂对象
@Override
public User clone() {
try {
return (User) super.clone(); // 浅拷贝!
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
// 问题:address只复制引用
User original = new User(1, "Alice", new Address("Shanghai"));
User copy = original.clone();
copy.getAddress().setCity("Beijing"); // 原对象也变了!
优缺点:
- ✅ 简单,使用内置接口
- ❌ 浅拷贝,容易出错
- ❌ 需要处理CloneNotSupportedException
方法2: Copy Constructor (深拷贝推荐)
public class User {
private int id;
private String name;
private Address address;
// 拷贝构造函数 - 推荐用于深拷贝
public User(User original) {
this.id = original.id;
this.name = original.name;
// ✅ 深拷贝Address
this.address = new Address(original.address);
}
}
public class Address {
private String city;
private String street;
public Address(Address original) {
this.city = original.city;
this.street = original.street;
}
}
// 使用
User original = new User(1, "Alice", new Address("Shanghai", "Nanjing Rd"));
User copy = new User(original); // 深拷贝
copy.getAddress().setCity("Beijing"); // 安全!不影响原对象
assert original.getAddress().getCity().equals("Shanghai"); // ✅
优缺点:
- ✅ 明确控制哪些字段拷贝
- ✅ 天然支持深拷贝
- ✅ 类型安全
- ❌ 需要为每个类写构造函数
方法3: Builder Pattern Clone (创建过程中克隆)
public class User {
private int id;
private String name;
private Address address;
private List<Role> roles;
// 私有构造,通过Builder创建
private User(UserBuilder builder) {
this.id = builder.id;
this.name = builder.name;
this.address = builder.address;
this.roles = builder.roles;
}
// Clone通过Builder
public User clone() {
return new UserBuilder(this).build();
}
// Builder支持部分修改
public static class UserBuilder {
private int id;
private String name;
private Address address;
private List<Role> roles;
public UserBuilder(User original) {
this(original.id);
this.name = original.name;
this.address = original.address.clone(); // 深拷贝
this.roles = new ArrayList<>(original.roles); // 深拷贝列表
}
public UserBuilder(int id) {
this.id = id;
this.roles = new ArrayList<>();
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder addRole(Role role) {
this.roles.add(role);
return this;
}
public User build() {
return new User(this);
}
}
}
// 使用:克隆并修改
User original = buildUser();
User modified = new UserBuilder(original)
.name("Bob") // 修改name
.addRole(Role.ADMIN) // 增加role
.build();
assert original.getName().equals("Alice"); // 原对象不变
assert modified.getName().equals("Bob"); // 新对象修改了
优缺点:
- ✅ 灵活,可部分修改
- ✅ 类型安全
- ✅ 深拷贝
- ❌ 代码较多
方法4: 深拷贝注册表 (动态克隆 + 缓存)
public class PrototypeRegistry {
private Map<String, Object> prototypes = new HashMap<>();
public void register(String key, Object prototype) {
prototypes.put(key, prototype);
}
// ✅ 深拷贝工厂方法
public <T> T clone(String key) {
Object prototype = prototypes.get(key);
if (prototype == null) {
throw new IllegalArgumentException("未知的原型: " + key);
}
return deepClone(prototype);
}
// 通用深拷贝 - 通过序列化
@SuppressWarnings("unchecked")
private <T> T deepClone(Object obj) {
try {
// 序列化 + 反序列化 = 完整深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
T cloned = (T) ois.readObject();
ois.close();
return cloned;
} catch (Exception e) {
throw new RuntimeException("深拷贝失败", e);
}
}
}
// 使用
PrototypeRegistry registry = new PrototypeRegistry();
registry.register("admin-user", new User(999, "Admin", roleList));
User cloned = registry.clone("admin-user"); // 深拷贝
cloned.setName("Modified Admin");
assert registry.clone("admin-user").getName().equals("Admin"); // ✅ 独立
优缺点:
- ✅ 通用的深拷贝
- ✅ 无需为每个类定制
- ❌ 序列化开销
- ❌ 类必须Serializable
Prototype的5种变体
变体1: 对象池 + Prototype
// 场景:频繁创建临时对象
public class ObjectPool<T> {
private Queue<T> available = new LinkedList<>();
private List<T> inUse = new ArrayList<>();
private T prototype;
public ObjectPool(T prototype, int poolSize) {
this.prototype = prototype;
for (int i = 0; i < poolSize; i++) {
available.offer(deepClone(prototype));
}
}
public T borrow() {
T obj = available.isEmpty() ?
deepClone(prototype) :
available.poll();
inUse.add(obj);
return obj;
}
public void release(T obj) {
inUse.remove(obj);
reset(obj); // 重置状态
available.offer(obj);
}
}
// 使用:数据库连接池
User templateUser = new User();
ObjectPool<User> pool = new ObjectPool<>(templateUser, 10);
User user = pool.borrow();
// 使用user
pool.release(user);
变体2: 撤销/重做系统
public class CommandWithPrototype {
private Stack<GameState> undoStack = new Stack<>();
private Stack<GameState> redoStack = new Stack<>();
public void executeCommand(GameCommand cmd) {
// 在执行前保存状态
GameState snapshot = gameState.deepClone();
undoStack.push(snapshot);
redoStack.clear();
// 执行命令
cmd.execute();
}
public void undo() {
if (undoStack.isEmpty()) return;
// 保存当前状态用于重做
redoStack.push(gameState.deepClone());
// 恢复之前状态
gameState = undoStack.pop();
}
public void redo() {
if (redoStack.isEmpty()) return;
undoStack.push(gameState.deepClone());
gameState = redoStack.pop();
}
}
变体3: 动态对象工厂
public class DynamicObjectFactory {
private Map<Class<?>, Object> templates = new HashMap<>();
public <T> void registerTemplate(Class<T> type, T template) {
templates.put(type, template);
}
public <T> T create(Class<T> type) {
Object template = templates.get(type);
if (template == null) {
throw new IllegalArgumentException("未注册: " + type);
}
return type.cast(deepClone(template));
}
}
// 使用
DynamicObjectFactory factory = new DynamicObjectFactory();
factory.registerTemplate(User.class, defaultUser);
factory.registerTemplate(Product.class, defaultProduct);
User user = factory.create(User.class);
Product product = factory.create(Product.class);
变体4: 缓存快照
public class CachedPrototype<T> {
private T current;
private T cached;
private long cacheTime;
private static final long TTL = 60000; // 1分钟
public CachedPrototype(T initial) {
this.current = initial;
this.cached = deepClone(initial);
}
public T getCurrent() {
return current;
}
public T getSnapshot() {
// 快速返回缓存
if (isCacheValid()) {
return deepClone(cached);
}
// 更新缓存
this.cached = deepClone(current);
this.cacheTime = System.currentTimeMillis();
return deepClone(cached);
}
private boolean isCacheValid() {
return System.currentTimeMillis() - cacheTime < TTL;
}
}
变体5: 原型链(Prototype Chain)
// JavaScript原型链的Java实现
public class PrototypeChain {
private Map<String, Object> own = new HashMap<>();
private PrototypeChain parent;
public PrototypeChain(PrototypeChain parentPrototype) {
this.parent = parentPrototype;
}
public Object get(String key) {
// 先查找自己的属性
if (own.containsKey(key)) {
return own.get(key);
}
// 再查找父原型
if (parent != null) {
return parent.get(key);
}
return null;
}
public void set(String key, Object value) {
own.put(key, value);
}
}
// 使用:模拟JavaScript对象
PrototypeChain objectProto = new PrototypeChain(null);
objectProto.set("toString", "Object.prototype.toString");
PrototypeChain arrayProto = new PrototypeChain(objectProto);
arrayProto.set("push", "Array.prototype.push");
assert arrayProto.get("toString") != null; // 继承
4个常见问题的深度解决
问题1: 浅拷贝陷阱
症状: 拷贝后修改嵌套对象,原对象也变了
反面示例(❌):
public class ShallowCloneBad {
private Address address;
public ShallowCloneBad clone() {
ShallowCloneBad copy = new ShallowCloneBad();
copy.address = this.address; // ❌ 浅拷贝
return copy;
}
}
// 灾难
ShallowCloneBad original = new ShallowCloneBad(new Address("Shanghai"));
ShallowCloneBad copy = original.clone();
copy.getAddress().setCity("Beijing");
assert original.getAddress().getCity().equals("Beijing"); // 原对象变了!
解决方案(✅):
public class DeepCloneGood {
private Address address;
public DeepCloneGood deepClone() {
DeepCloneGood copy = new DeepCloneGood();
copy.address = this.address.deepClone(); // ✅ 深拷贝
return copy;
}
}
// 安全
DeepCloneGood original = new DeepCloneGood(new Address("Shanghai"));
DeepCloneGood copy = original.deepClone();
copy.getAddress().setCity("Beijing");
assert original.getAddress().getCity().equals("Shanghai"); // ✅ 原对象不变
问题2: 序列化的复杂对象
症状: 包含非Serializable字段的对象无法克隆
反面示例(❌):
public class UnserializableObject implements Serializable {
private DatabaseConnection conn; // ❌ 不能序列化!
// 序列化会失败
}
解决方案(✅):
public class SerializableWorkaround {
private DatabaseConnection conn;
// 自定义序列化逻辑
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(conn.getURL()); // 只序列化URL
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
String url = (String) ois.readObject();
this.conn = new DatabaseConnection(url); // 重建连接
}
}
// 或者用Copy Constructor
public class CopyConstructorApproach {
private DatabaseConnection conn;
public CopyConstructorApproach(CopyConstructorApproach original) {
this.conn = new DatabaseConnection(original.conn.getURL());
}
}
问题3: 深拷贝的性能成本
症状: 对象很大,深拷贝很慢
分析:
对象大小 深拷贝成本 何时使用
─────────────────────────────────
< 1MB 微不足道(<1ms) 总是用深拷贝
1-10MB 10-100ms 权衡,通常值得
10-100MB 100ms-1s 考虑浅拷贝 + COW
> 100MB > 1s 用COW或共享存储
优化方案:
// 方案1:Copy-On-Write (COW)
public class COWList<T> implements Iterable<T> {
private List<T> data;
private boolean isClone = false;
public void add(T item) {
if (isClone) {
// 已经复制,直接修改
data.add(item);
} else {
// 第一次修改时才复制
data = new ArrayList<>(data);
data.add(item);
isClone = true;
}
}
public COWList<T> clone() {
// 不复制数据,只是标记
COWList<T> copy = new COWList<>();
copy.data = this.data;
copy.isClone = false;
return copy;
}
}
// 方案2:指数退避(Exponential Backoff)
byte[] buffer = new byte[10_000_000]; // 10MB对象
byte[] copy = buffer.clone(); // 自动优化,很快
// 方案3:选择性深拷贝
public class SelectiveDeepClone {
private List<Item> items; // 通常不变,浅拷贝
private Map<String, Config> configs; // 经常变化,深拷贝
public SelectiveDeepClone clone() {
SelectiveDeepClone copy = new SelectiveDeepClone();
copy.items = this.items; // ⚠️ 浅拷贝
copy.configs = deepCopy(this.configs); // ✅ 深拷贝
return copy;
}
}
问题4: 循环引用处理
症状: 对象A包含B,B包含A,导致深拷贝死循环
反面示例(❌):
public class Node {
public Node next;
public Node deepClone() {
Node copy = new Node();
copy.next = this.next.deepClone(); // ❌ 死循环!
return copy;
}
}
// 甚至可能是间接的:A→B→C→A
解决方案(✅):
public class NodeWithCycleHandling {
public Node next;
public Node deepClone() {
return deepClone(new IdentityHashMap<>());
}
private Node deepClone(Map<Node, Node> visited) {
// 如果已访问,返回已克隆的副本
if (visited.containsKey(this)) {
return visited.get(this);
}
Node copy = new Node();
visited.put(this, copy); // 记录,防止重复
if (this.next != null) {
copy.next = this.next.deepClone(visited);
}
return copy;
}
}
// 序列化方式自动处理
class SerializableNode implements Serializable {
public SerializableNode next;
public SerializableNode deepClone() {
try {
// Java序列化自动处理循环引用
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (SerializableNode) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
最佳实践
- ✅ 默认使用深拷贝 - 除非确定浅拷贝足够
- ✅ 使用Copy Constructor - 比Cloneable更清晰
- ✅ 文档化克隆深度 - 说明复制了什么
- ✅ 性能测试 - 大对象要测试克隆成本
- ✅ 使用不可变对象 - 减少克隆需求
何时避免使用
- ❌ 创建成本很低 - 直接new比克隆快
- ❌ 对象是无状态的 - 无需克隆
- ❌ 改用工厂模式更清晰 - 有复杂选择逻辑时
- ❌ 改用Builder模式更好 - 需要部分定制时