Skip to content

原型模式 (Prototype Pattern) ​

模式定义 ​

原型模式是一种创建型设计模式,通过复制(克隆)已有实例来创建新对象,而不是通过 new 关键字实例化。

核心要点 ​

  • 实现 Cloneable 接口
  • 重写 clone() 方法
  • 区分浅拷贝和深拷贝

浅拷贝 vs 深拷贝 ​

特性浅拷贝深拷贝
基本类型复制值复制值
引用类型复制引用复制整个对象
性能快较慢
安全性可能有副作用更安全

实现方式 ​

1. Cloneable 接口(浅拷贝) ​

java
public class Address implements Cloneable, Serializable {
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    // 实现浅拷贝
    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    // getter/setter 省略

    @Override
    public String toString() {
        return "Address{city='" + city + "', street='" + street + "'}";
    }
}

public class Employee implements Cloneable, Serializable {
    private String name;
    private double salary;
    private Address address; // 引用类型

    public Employee(String name, double salary, Address address) {
        this.name = name;
        this.salary = salary;
        this.address = address;
    }

    // 浅拷贝
    @Override
    public Employee clone() {
        try {
            return (Employee) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    // getter/setter 省略

    @Override
    public String toString() {
        return "Employee{name='" + name + "', salary=" + salary +
                ", address=" + address + '}';
    }
}

// 测试浅拷贝
public class ShallowCopyDemo {
    public static void main(String[] args) {
        Address address = new Address("北京", "朝阳区");
        Employee emp1 = new Employee("张三", 10000, address);

        // 克隆对象
        Employee emp2 = emp1.clone();

        System.out.println("修改前:");
        System.out.println("emp1: " + emp1);
        System.out.println("emp2: " + emp2);

        // 修改克隆对象的基本类型
        emp2.salary = 20000;
        // 修改克隆对象的引用类型
        emp2.address.city = "上海";

        System.out.println("\n修改后:");
        System.out.println("emp1: " + emp1); // address.city 也变成了"上海"
        System.out.println("emp2: " + emp2);

        // 验证引用是否相同
        System.out.println("\n地址是否为同一对象: " +
                (emp1.address == emp2.address)); // true,浅拷贝
    }
}

2. 深拷贝 - 手动实现 ​

java
public class DeepCloneAddress implements Cloneable, Serializable {
    private String city;
    private String street;

    public DeepCloneAddress(String city, String street) {
        this.city = city;
        this.street = street;
    }

    @Override
    public DeepCloneAddress clone() {
        try {
            return (DeepCloneAddress) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    // getter/setter 省略
}

public class DeepCloneEmployee implements Cloneable, Serializable {
    private String name;
    private double salary;
    private DeepCloneAddress address;

    public DeepCloneEmployee(String name, double salary, DeepCloneAddress address) {
        this.name = name;
        this.salary = salary;
        this.address = address;
    }

    // 深拷贝 - 手动克隆每个引用类型
    @Override
    public DeepCloneEmployee clone() {
        try {
            DeepCloneEmployee cloned = (DeepCloneEmployee) super.clone();
            // 对引用类型也进行克隆
            cloned.address = this.address.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toString() {
        return "DeepCloneEmployee{name='" + name + "', salary=" + salary +
                ", address=" + address + '}';
    }
}

3. 深拷贝 - 序列化实现 ​

通过序列化和反序列化实现完全深拷贝。

java
public class SerializationCopy {
    // 通用深拷贝方法
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCopy(T object) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(object);
            oos.flush();

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("深拷贝失败", e);
        }
    }
}

// 使用
// Employee emp2 = SerializationCopy.deepCopy(emp1);
// 注意:所有引用的类都需要实现 Serializable 接口

4. 深拷贝 - JSON 序列化 ​

使用 JSON 工具库实现深拷贝(需要额外依赖)。

java
// 使用 Jackson
public class JsonCopy {
    private static final ObjectMapper mapper = new ObjectMapper();

    @SuppressWarnings("unchecked")
    public static <T> T deepCopy(T object, Class<T> clazz) {
        try {
            String json = mapper.writeValueAsString(object);
            return mapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new RuntimeException("JSON 深拷贝失败", e);
        }
    }
}

// 使用
// Employee emp2 = JsonCopy.deepCopy(emp1, Employee.class);

5. 原型注册表 ​

管理多个原型实例,支持按名称克隆。

java
public class PrototypeRegistry {
    // 原型注册表
    private static final Map<String, Cloneable> prototypes = new HashMap<>();

    // 注册原型
    public static void register(String name, Cloneable prototype) {
        prototypes.put(name, prototype);
    }

    // 按名称克隆
    @SuppressWarnings("unchecked")
    public static <T> T create(String name) {
        Cloneable prototype = prototypes.get(name);
        if (prototype == null) {
            throw new IllegalArgumentException("未找到原型: " + name);
        }
        return (T) prototype.clone();
    }

    // 移除原型
    public static void remove(String name) {
        prototypes.remove(name);
    }
}

// 使用
public class PrototypeRegistryDemo {
    public static void main(String[] args) {
        // 注册原型
        PrototypeRegistry.register("employee",
                new Employee("默认员工", 5000, new Address("北京", "默认地址")));
        PrototypeRegistry.register("manager",
                new Employee("默认经理", 15000, new Address("上海", "管理地址")));

        // 克隆原型
        Employee emp1 = PrototypeRegistry.create("employee");
        Employee emp2 = PrototypeRegistry.create("manager");

        System.out.println("普通员工: " + emp1);
        System.out.println("经理: " + emp2);
    }
}

JDK 中的原型模式 ​

HashMap.clone() ​

java
HashMap<String, Integer> original = new HashMap<>();
original.put("key1", 1);

// 浅拷贝
HashMap<String, Integer> cloned = (HashMap<String, Integer>) original.clone();

ArrayList.clone() ​

java
ArrayList<String> list = new ArrayList<>();
list.add("Java");

ArrayList<String> cloned = (ArrayList<String>) list.clone();

实际应用 ​

  • Spring Bean: prototype 作用域的 Bean 每次请求都克隆一个新实例
  • 对象缓存: 缓存中存储原型,需要时克隆
  • 配置对象: 从模板配置克隆出多个相似配置
  • 图形编辑器: 复制图形对象
  • 游戏角色: 克隆角色模板

优缺点 ​

优点:

  • 性能好,比 new 创建对象快
  • 隐藏创建细节
  • 可以动态获取对象运行时状态

缺点:

  • 深拷贝实现复杂
  • 需要实现 Cloneable 接口
  • 对于包含循环引用的对象,克隆较复杂

注意事项 ​

  1. clone() 方法是浅拷贝,需要深拷贝时要手动实现
  2. 引用类型需要额外处理
  3. 序列化方式实现深拷贝要求所有类实现 Serializable
  4. 原型模式与工厂模式结合使用效果更好