一、为什么需要原型模式?
在传统对象创建方式中,我们通过new
关键字直接调用构造函数创建实例。但当遇到以下场景时:
- 对象初始化需要消耗大量资源(如数据库连接)
- 需要创建的对象与现有实例高度相似
- 希望屏蔽对象创建的复杂过程
原型模式通过克隆已有对象的方式创建新实例,相比直接创建具有以下优势:
- 避开重复的初始化过程
- 动态获取对象运行时状态
- 实现对象创建与使用的解耦
二、模式结构解析
plaintext
+----------------+ +-----------------+
| Prototype | | Client |
+----------------+ +-----------------+
|+ clone():Object|<>-------| |
+----------------+ +-----------------+
^
|
+------------------+
| ConcretePrototype|
+------------------+
| + clone():Object |
+------------------+
关键角色说明
- Prototype(抽象原型):声明克隆方法的接口
- ConcretePrototype(具体原型):实现克隆方法的具体类
- Client(客户端):通过调用原型对象的克隆方法创建新对象
三、深拷贝 vs 浅拷贝
类型 | 实现方式 | 特点 | 适用场景 |
---|---|---|---|
浅拷贝 | Object.clone()默认实现 | 复制基本类型,引用类型共享 | 对象无嵌套引用 |
深拷贝 | 手动实现或序列化方式 | 完全独立的对象副本 | 复杂对象结构 |
Java实现深拷贝的三种方式
java
// 方式1:手动克隆
public class DeepClone implements Cloneable {
private List<String> data = new ArrayList<>();
@Override
protected Object clone() throws CloneNotSupportedException {
DeepClone copy = (DeepClone)super.clone();
copy.data = new ArrayList<>(this.data); // 手动创建新集合
return copy;
}
}
// 方式2:序列化实现
public static <T extends Serializable> T deepClone(T obj) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 方式3:使用JSON序列化(推荐)
public static <T> T deepCloneByJson(T obj) {
Gson gson = new Gson();
String json = gson.toJson(obj);
return gson.fromJson(json, (Type) obj.getClass());
}
四、实战应用场景
1. 游戏开发 - 敌人生成
java
class Enemy implements Cloneable {
private String type;
private int hp;
private Weapon weapon;
public Enemy(String type, int hp, Weapon weapon) {
// 复杂的初始化过程(加载3D模型、AI配置等)
this.type = type;
this.hp = hp;
this.weapon = weapon;
}
@Override
public Enemy clone() {
try {
Enemy cloned = (Enemy) super.clone();
cloned.weapon = this.weapon.clone(); // 深拷贝武器对象
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
// 使用原型创建敌人
Enemy orcPrototype = new Enemy("Orc", 100, new Axe());
List<Enemy> army = new ArrayList<>();
for(int i=0; i<1000; i++){
army.add(orcPrototype.clone());
}
2. 配置对象复用
java
class AppConfig implements Cloneable {
private DatabaseConfig dbConfig;
private CacheConfig cacheConfig;
private boolean debugMode;
// 初始化需要从配置文件读取各种参数
public AppConfig() {
// 耗时操作(约2秒)
}
@Override
public AppConfig clone() {
return deepCloneByJson(this); // 使用JSON深拷贝
}
}
// 创建多个配置副本
AppConfig baseConfig = new AppConfig(); // 仅初始化一次
AppConfig testConfig = baseConfig.clone();
testConfig.setDebugMode(true);
AppConfig productionConfig = baseConfig.clone();
productionConfig.getDbConfig().setPoolSize(100);
五、性能对比测试
创建10000个复杂对象耗时比较:
| 创建方式 | 耗时(ms) |
|----------------|------------|
| new操作 | 2350 |
| 浅拷贝 | 12 |
| 深拷贝(手动) | 45 |
| 深拷贝(JSON) | 85 |
六、最佳实践指南
- 对象注册表优化
java
class PrototypeRegistry {
private static Map<String, Prototype> prototypes = new HashMap<>();
static {
prototypes.put("default", new ConcretePrototype());
prototypes.put("advanced", new AdvancedPrototype());
}
public static Prototype getPrototype(String type) {
return prototypes.get(type).clone();
}
}
- 结合其他模式
- 与工厂模式结合:创建克隆工厂
- 与备忘录模式结合:实现状态回滚
- 与组合模式结合:克隆复杂对象结构
- 注意事项
- 谨慎处理final字段
- 注意克隆构造函数不会被调用
- 使用Unsafe类实现高效克隆(需要处理安全限制)
java
public class UnsafeClone {
private static final Unsafe UNSAFE;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T clone(T obj) {
try {
T copy = (T) UNSAFE.allocateInstance(obj.getClass());
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
long offset = UNSAFE.objectFieldOffset(field);
Object value = UNSAFE.getObject(obj, offset);
UNSAFE.putObject(copy, offset, value);
}
return copy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
七、框架中的应用实例
Spring框架中的原型作用域
xml
<bean id="protoBean" class="com.example.PrototypeBean" scope="prototype"/>
JavaScript原型继承
javascript
class Vehicle {
constructor() { this.type = 'generic' }
clone() { return Object.create(Object.getPrototypeOf(this)) }
}
const car = new Vehicle();
const carClone = car.clone();
总结
原型模式是创建型模式中极具特色的实现方式,特别适用于以下场景:
- 需要创建的对象类型在运行时才能确定
- 需要动态加载类
- 需要避免使用分层次的工厂类来创建分层次的对象
- 当一个系统的产品有多个并列的等级结构时
在实际应用中,建议:
- 优先考虑浅拷贝,必要时使用深拷贝
- 对于复杂对象建议实现原型管理器
- 注意线程安全问题
- 结合具体语言特性优化实现
通过合理使用原型模式,可以显著提升系统性能,特别是在需要大量创建相似对象的场景下,性能提升可达10倍以上。但也要注意避免过度使用导致的代码复杂度增加,在简单对象创建场景下仍推荐使用传统的工厂方法。