一、模式定义
原型模式是一种创建型设计模式,允许通过复制现有对象(原型)来创建新对象,而不是通过构造函数创建。当直接创建对象的成本较高时,这种模式特别有用。
二、Java源代码实现
1. 基础实现方式
方式一:实现Cloneable接口(浅克隆)
// 1. 原型接口
public interface Prototype extends Cloneable {
Prototype clone() throws CloneNotSupportedException;
void display();
}
// 2. 具体原型类
public class ConcretePrototype implements Prototype {
private String name;
private int id;
private List<String> tags; // 引用类型字段
private Date createTime;
public ConcretePrototype(String name, int id) {
this.name = name;
this.id = id;
this.tags = new ArrayList<>();
this.createTime = new Date();
// 模拟复杂初始化过程
System.out.println("执行复杂对象初始化...");
try {
Thread.sleep(1000); // 模拟耗时初始化
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void addTag(String tag) {
tags.add(tag);
}
@Override
public Prototype clone() throws CloneNotSupportedException {
// 使用Object的clone()方法实现浅克隆
return (ConcretePrototype) super.clone();
}
@Override
public void display() {
System.out.println("ConcretePrototype [name=" + name + ", id=" + id
+ ", tags=" + tags + ", createTime=" + createTime + "]");
}
// 用于测试引用的setter
public void setTags(List<String> tags) {
this.tags = tags;
}
}
方式二:深克隆实现
// 深克隆原型类
public class DeepPrototype implements Serializable, Cloneable {
private String name;
private int id;
private List<String> tags;
private Date createTime;
private InnerObject innerObject; // 另一个引用对象
public DeepPrototype(String name, int id) {
this.name = name;
this.id = id;
this.tags = new ArrayList<>();
this.createTime = new Date();
this.innerObject = new InnerObject("初始值");
}
// 方法1:通过序列化实现深克隆
public DeepPrototype deepCloneBySerialization() {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (DeepPrototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 方法2:手动实现深克隆
@Override
public DeepPrototype clone() {
try {
DeepPrototype clone = (DeepPrototype) super.clone();
// 克隆引用类型字段
if (this.tags != null) {
clone.tags = new ArrayList<>(this.tags);
}
if (this.createTime != null) {
clone.createTime = (Date) this.createTime.clone();
}
if (this.innerObject != null) {
clone.innerObject = this.innerObject.clone();
}
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public void addTag(String tag) {
tags.add(tag);
}
public void display() {
System.out.println("DeepPrototype [name=" + name + ", id=" + id
+ ", tags=" + tags + ", createTime=" + createTime
+ ", innerObject=" + innerObject + "]");
}
// 内部类
static class InnerObject implements Serializable, Cloneable {
private String data;
public InnerObject(String data) {
this.data = data;
}
@Override
public InnerObject clone() {
try {
return (InnerObject) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
@Override
public String toString() {
return "InnerObject{data='" + data + "'}";
}
public void setData(String data) {
this.data = data;
}
}
}
2. 原型管理器(注册表模式)
// 原型管理器
public class PrototypeRegistry {
private Map<String, Prototype> prototypes = new HashMap<>();
public PrototypeRegistry() {
// 预置一些原型对象
prototypes.put("default", new ConcretePrototype("默认对象", 1));
prototypes.put("advanced", new ConcretePrototype("高级对象", 2));
}
public void addPrototype(String key, Prototype prototype) {
prototypes.put(key, prototype);
}
public Prototype getPrototype(String key) {
try {
return prototypes.get(key).clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
3. 客户端使用示例
public class PrototypeClient {
public static void main(String[] args) {
System.out.println("=== 浅克隆示例 ===");
testShallowClone();
System.out.println("\n=== 深克隆示例 ===");
testDeepClone();
System.out.println("\n=== 原型管理器示例 ===");
testPrototypeRegistry();
}
private static void testShallowClone() {
try {
ConcretePrototype original = new ConcretePrototype("原始对象", 100);
original.addTag("标签1");
original.addTag("标签2");
ConcretePrototype cloned = (ConcretePrototype) original.clone();
System.out.println("原始对象: ");
original.display();
System.out.println("克隆对象: ");
cloned.display();
// 修改原始对象的引用类型字段
original.addTag("标签3");
System.out.println("\n修改原始对象的tags后:");
System.out.println("原始对象tags: " + original.getTags());
System.out.println("克隆对象tags: " + cloned.getTags()); // 浅克隆会受影响
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
private static void testDeepClone() {
DeepPrototype original = new DeepPrototype("深克隆对象", 200);
original.addTag("初始标签");
// 通过序列化深克隆
DeepPrototype clonedBySerial = original.deepCloneBySerialization();
// 手动深克隆
DeepPrototype clonedManual = original.clone();
System.out.println("原始对象: ");
original.display();
System.out.println("序列化深克隆对象: ");
clonedBySerial.display();
System.out.println("手动深克隆对象: ");
clonedManual.display();
// 修改原始对象
original.addTag("新标签");
System.out.println("\n修改原始对象后对比:");
System.out.println("原始对象tags: " + original.getTags());
System.out.println("深克隆对象tags: " + clonedManual.getTags()); // 不受影响
}
private static void testPrototypeRegistry() {
PrototypeRegistry registry = new PrototypeRegistry();
// 获取预置的原型
Prototype defaultObj = registry.getPrototype("default");
Prototype advancedObj = registry.getPrototype("advanced");
System.out.println("从注册表获取的默认对象:");
defaultObj.display();
System.out.println("从注册表获取的高级对象:");
advancedObj.display();
// 添加自定义原型
registry.addPrototype("custom", new ConcretePrototype("自定义对象", 999));
Prototype customObj = registry.getPrototype("custom");
System.out.println("从注册表获取的自定义对象:");
customObj.display();
}
}
三、应用场景深度解析
1. 对象创建成本高的场景
- 数据库查询结果缓存:查询结果作为原型,后续相似查询直接克隆修改
- 复杂配置对象:系统配置、游戏角色属性等
- 大型文档对象:Word、Excel文档模板
2. 需要避免构造器约束的场景
- 对象构造需要权限或复杂依赖
- 对象状态由运行时决定而非编译时
- 需要绕过构造函数创建对象
3. 动态创建对象的场景
- 图形编辑器:复制图形元素
- 游戏开发:复制NPC、武器、技能
- 工作流系统:复制流程实例
4. 具体应用案例
// 案例1:缓存数据库查询结果
public class QueryResultCache {
private Map<String, QueryResult> cache = new HashMap<>();
public QueryResult getCachedResult(String query) {
QueryResult prototype = cache.get(query);
if (prototype != null) {
return prototype.clone(); // 返回克隆,避免原始数据被修改
}
return null;
}
}
// 案例2:游戏中的武器系统
public abstract class Weapon implements Cloneable {
protected String name;
protected int damage;
protected List<Enchantment> enchantments;
public Weapon clone() {
try {
Weapon cloned = (Weapon) super.clone();
cloned.enchantments = new ArrayList<>(this.enchantments);
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
四、优缺点深度解析
优点
-
性能优化
- 避免重复的初始化代码
- 绕过复杂的构造函数
- 适用于创建成本高的对象
-
灵活性高
- 运行时动态创建对象
- 不依赖具体类
- 可结合工厂模式增强
-
简化对象创建
- 隐藏创建细节
- 客户端代码简洁
- 易于扩展
-
保护原始对象
- 克隆不影响原型
- 支持撤销操作
- 线程安全的基础
缺点
-
深克隆复杂性
- 循环引用问题
- 克隆链管理困难
- 性能开销(序列化方式)
-
继承限制
- 必须实现Cloneable接口
- 子类需要重写clone方法
- 破坏封装性
-
内存问题
- 大对象克隆的内存消耗
- 需要管理克隆生命周期
- 可能的内存泄漏
-
设计复杂性
- 需要区分离散对象
- 原型注册表维护
- 版本管理困难
五、使用要点与最佳实践
1. 浅克隆 vs 深克隆的选择
// 决策矩阵
- 浅克隆:基本类型+不可变对象 ✅
- 深克隆:包含可变引用对象 ✅
- 混合克隆:部分深克隆+部分浅克隆 ✅
2. 正确的clone方法实现
@Override
public MyClass clone() {
try {
MyClass cloned = (MyClass) super.clone();
// 手动处理引用类型
cloned.listField = new ArrayList<>(this.listField);
cloned.dateField = (Date) this.dateField.clone();
cloned.objectField = this.objectField.clone();
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 不应该发生
}
}
3. 原型注册表的设计模式
public abstract class PrototypeRegistry<T extends Prototype> {
private Map<String, T> registry = new ConcurrentHashMap<>();
public void register(String key, T prototype) {
registry.put(key, prototype);
}
public T create(String key) {
T prototype = registry.get(key);
if (prototype == null) {
throw new IllegalArgumentException("未找到原型: " + key);
}
return prototype.clone();
}
// 支持延迟加载
public abstract void initialize();
}
4. 性能优化策略
// 1. 使用对象池
public class PrototypePool {
private Map<Class<?>, Queue<Object>> pool = new HashMap<>();
public <T> T acquire(Class<T> clazz) {
Queue<Object> queue = pool.get(clazz);
if (queue != null && !queue.isEmpty()) {
return clazz.cast(queue.poll());
}
return createNew(clazz);
}
public void release(Object obj) {
Queue<Object> queue = pool.computeIfAbsent(
obj.getClass(), k -> new LinkedList<>());
queue.offer(obj);
}
}
// 2. 使用Copy-on-Write优化
public class CowPrototype implements Cloneable {
private volatile Data data;
public CowPrototype clone() {
CowPrototype clone = (CowPrototype) super.clone();
// 延迟复制,只在修改时复制
return clone;
}
public void modify() {
synchronized (this) {
data = data.copy(); // 复制并替换
}
}
}
5. 线程安全考虑
// 线程安全的原型管理器
public class ThreadSafePrototypeRegistry {
private final Map<String, Prototype> prototypes =
Collections.synchronizedMap(new HashMap<>());
public Prototype getPrototype(String key) {
Prototype prototype = prototypes.get(key);
if (prototype == null) {
synchronized (this) {
prototype = prototypes.get(key);
if (prototype == null) {
prototype = createPrototype(key);
prototypes.put(key, prototype);
}
}
}
return prototype.clone();
}
}
6. 测试策略
@Test
public void testPrototypeCloning() {
// 1. 测试浅克隆引用问题
Original original = new Original();
original.getList().add("item");
Original cloned = original.clone();
cloned.getList().add("new item");
// 浅克隆时,两个对象共享list
assertNotSame(original.getList(), cloned.getList()); // 深克隆应通过
// 2. 测试不可变对象
String immutable = original.getName();
cloned.setName("changed");
assertEquals("original", original.getName()); // 应保持不变
// 3. 测试性能
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
original.clone();
}
long time = System.currentTimeMillis() - start;
assertTrue(time < 1000); // 克隆应比构造快
}
六、与其他模式的比较
| 模式 | 相似点 | 不同点 | 适用场景 |
|---|---|---|---|
| 工厂模式 | 都创建对象 | 工厂模式通过子类创建,原型模式通过克隆 | 对象创建逻辑复杂时用工厂,对象本身复杂时用原型 |
| 建造者模式 | 都创建复杂对象 | 建造者逐步构建,原型直接复制完整对象 | 需要不同表示时用建造者,需要相同结构时用原型 |
| 单例模式 | 都控制对象创建 | 单例确保唯一实例,原型产生多个副本 | 全局唯一用单例,多副本用原型 |
| 享元模式 | 都重用对象 | 享元共享内部状态,原型复制完整状态 | 大量细粒度对象用享元,少量复杂对象用原型 |
七、实际应用示例
示例1:Spring框架中的原型作用域
@Component
@Scope("prototype") // 每次注入都是新实例
public class ServicePrototype {
// ...
}
// 使用
@Service
public class UserService {
@Autowired
private ApplicationContext context;
public void process() {
// 每次获取新实例
ServicePrototype prototype = context.getBean(ServicePrototype.class);
}
}
示例2:图形编辑器
// 图形基类
public abstract class Graphic implements Cloneable {
protected int x, y;
protected Color color;
public Graphic clone() {
try {
Graphic cloned = (Graphic) super.clone();
cloned.color = this.color.clone();
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public abstract void draw();
public abstract Graphic duplicate();
}
// 具体图形
public class Rectangle extends Graphic {
private int width, height;
@Override
public Rectangle clone() {
Rectangle cloned = (Rectangle) super.clone();
return cloned;
}
@Override
public void draw() {
System.out.println("绘制矩形");
}
@Override
public Graphic duplicate() {
return this.clone();
}
}
八、注意事项
- final字段问题:clone方法无法修改final字段
- 构造函数不执行:克隆不会调用构造函数
- 继承链维护:需要确保整个继承链正确实现clone
- 性能监控:深克隆大对象时监控内存使用
- 版本兼容:原型对象修改时要考虑已存在的克隆对象
九、总结
原型模式是一种强大的对象创建模式,特别适用于:
- 对象创建成本高
- 需要避免构造函数约束
- 系统需要动态创建对象
- 需要保护原始对象不被修改
正确使用原型模式需要:
- 根据需求选择浅克隆或深克隆
- 合理设计原型注册表
- 注意线程安全问题
- 考虑性能影响
- 编写充分的测试用例
在Java生态中,原型模式广泛应用于Spring框架、GUI系统、游戏开发等领域,是每个Java开发者必须掌握的设计模式之一。