原型模式详解

发布于:2025-03-20 ⋅ 阅读:(21) ⋅ 点赞:(0)

以下是一个结合原型模式解决实际开发问题的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对象地址或者对象序号。


网站公告

今日签到

点亮在社区的每一天
去签到