以下是一个结合原型模式解决实际开发问题的Java实现案例,涵盖深拷贝实现、性能优化、动态配置克隆等场景需求,附带逐行中文注释:
场景描述
实现一个可复用的游戏角色模板系统,需满足:
快速克隆:避免重复执行角色初始化(如加载3D模型、读取配置文件)
深度克隆:确保克隆对象与原对象完全独立(包括嵌套对象)
动态修改:克隆后允许局部修改属性(如武器、皮肤)
性能优化:避免重复IO操作(如模型加载)
完整代码实现
import java.io.*;
/**
* 游戏角色原型(实现Cloneable接口)
*/
class GameCharacter implements Cloneable, Serializable {
private String name;
private Weapon weapon; // 武器对象(自定义类)
private byte[] model3D; // 3D模型数据(模拟大对象)
public GameCharacter(String name, String modelPath) {
this.name = name;
load3DModel(modelPath); // 模拟耗时IO操作
this.weapon = new Weapon("默认武器"); // 初始化默认武器
}
// 模拟从文件加载3D模型(耗时操作)
private void load3DModel(String path) {
System.out.println("正在加载模型:" + path);
// 假设此处读取文件到byte[]
this.model3D = new byte[1024 * 1024]; // 模拟1MB模型数据
}
/**
* 浅拷贝实现(不推荐,拷贝引用,原对象变化,拷贝对象也会变化,经典八股问题之一)
*/
@Override
public GameCharacter clone() throws CloneNotSupportedException {
return (GameCharacter) super.clone();
}
/**
* 深拷贝实现(通过序列化)
*/
public GameCharacter deepClone() {
ByteArrayOutputStream bos;
ObjectOutputStream oos;
ByteArrayInputStream bis;
ObjectInputStream ois;
try {
// 将对象写入流,这里需要显式关闭,final关闭,也可以使用try-with-resources
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 从流中读取对象,这里需要显式关闭
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (GameCharacter) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("克隆失败", e);
} finally {
//..关闭流;
}
}
// 修改武器(测试用)
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
// 显示角色信息
public void showInfo() {
System.out.printf("角色:%s | 武器:%s | 模型哈希:%d%n",
name, weapon.getName(), model3D.hashCode());
}
// 武器类(必须实现Serializable)
static class Weapon implements Serializable {
private String name;
public Weapon(String name) { this.name = name; }
public String getName() { return name; }
}
}
/**
* 原型管理器(存储常用原型)
*/
class PrototypeManager {
private static final java.util.Map<String, GameCharacter> prototypes = new java.util.HashMap<>();
// 预加载原型
static {
prototypes.put("warrior", new GameCharacter("战士", "models/warrior.obj"));
prototypes.put("mage", new GameCharacter("法师", "models/mage.obj"));
}
// 获取原型副本
public static GameCharacter getCharacter(String type) {
GameCharacter proto = prototypes.get(type);
if (proto == null) throw new IllegalArgumentException("未知角色类型");
return proto.deepClone(); // 返回深拷贝副本
}
}
// 测试代码
public class PrototypeDemo {
public static void main(String[] args) throws Exception {
// 1. 基础克隆测试
GameCharacter orig = new GameCharacter("原型角色", "models/base.obj");
orig.setWeapon(new GameCharacter.Weapon("默认武器"));
GameCharacter shallowCopy = orig.clone();
GameCharacter deepCopy = orig.deepClone();
System.out.println("-- 浅拷贝 vs 深拷贝 --");
orig.showInfo(); // 角色:原型角色 | 武器:默认武器 | 模型哈希
shallowCopy.showInfo(); // 模型哈希相同(共享同一模型数据)
deepCopy.showInfo(); // 模型哈希不同(独立数据),且只会拷贝引用
// 2. 修改武器测试
shallowCopy.weapon.name = "新武器";
deepCopy.weapon.name = "超级武器";
System.out.println("\n-- 武器修改后 --");
//这里由于浅拷贝的对象改变了值,武器对象在两个对象里面属于同一个引用,原始对象的武器发生改变
//深拷贝就会改变引用,不会影响原始对象,深拷贝就可以复制一个全新对象,所有均不影响原始对象
orig.showInfo(); // 武器:新武器 模型哈希:558638686
shallowCopy.showInfo(); // 武器:新武器 模型哈希:558638686
deepCopy.showInfo(); // 武器:超级武器 模型哈希:1268447657
// 3. 使用原型管理器
GameCharacter warrior1 = PrototypeManager.getCharacter("warrior");
GameCharacter warrior2 = PrototypeManager.getCharacter("warrior");
System.out.println("\n-- 原型管理器生成对象 --");
System.out.println("是否为同一模型:" +
(warrior1.hashCode() == warrior2.hashCode())); // false(深拷贝)
}
}
真实场景问题
1.当系统中需要创建大量相似对象,且这些对象之间的差异较小时,可以使用原型模式来复制一个已有对象,然后根据需要修改差异部分,减少对象创建的开销。
2.在数据库中获取的数据对象可能需要在业务逻辑中进行多次使用,而不希望频繁地从数据库中读取相同的数据。使用原型模式可以在第一次获取数据时创建原型对象,后续需要相似数据时直接复制原型对象,减少数据库交互。
3.当对象的创建过程较为复杂,包含多个步骤或依赖关系时,可以通过原型模式创建一个已经初始化的对象,然后在需要时进行复制,避免重复的初始化过程。
4.如果系统中有一些配置对象,这些对象的配置信息可能在运行时动态发生变化,使用原型模式可以在初始状态下创建一个配置对象的原型,然后根据需要进行复制并动态修改配置信息。
一句话总结
原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来创建新对象,而不需要使用显式的构造函数调用来创建。建议学习原型可以使用debug看一下java对象地址或者对象序号。