以下是一个结合原型模式解决实际开发问题的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模型数据}/*** 浅拷贝实现(不推荐,拷贝引用,原对象变化,拷贝对象也会变化,经典八股问题之一)*/@Overridepublic GameCharacter clone() throws CloneNotSupportedException {return (GameCharacter) super.clone();}/*** 深拷贝实现(通过序列化)*/public GameCharacter deepClone() {ByteArrayOutputStream bos;ObjectOutputStream oos;ByteArrayInputStream bis;ObjectInputStream ois;try {// 将对象写入流,这里需要显式关闭,final关闭,也可以使用try-with-resourcesbos = 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(); // 武器:新武器 模型哈希:558638686shallowCopy.showInfo(); // 武器:新武器 模型哈希:558638686deepCopy.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对象地址或者对象序号。