什么是原型模式?

原型模式通过复制(克隆)一个已有的对象来创建新对象,而不是通过new关键字创建。它就像是复印机

  • 你有一份原稿(原型对象)
  • 放进复印机,得到一份一模一样的副本(克隆对象)
  • 可以在副本上修改,不影响原稿

主要解决什么问题?

原型模式主要解决以下问题:

  1. 对象创建成本高:有些对象的创建过程很复杂或耗时
  2. 需要大量相似对象:避免重复进行复杂的初始化
  3. 对象状态复杂:希望保留对象的部分状态
  4. 动态创建:在运行时不知道具体类型
  5. 避免子类泛滥:工厂模式需要为每个产品创建工厂类

何时使用原型模式?

当你遇到以下场景时,考虑使用原型模式:

  • 对象初始化操作复杂(如数据库查询、网络请求)
  • 需要创建大量相似对象(如游戏中的子弹、粒子)
  • 对象状态需要保留(如撤销操作时的快照)
  • 系统需要独立于对象的创建方式
  • 运行时动态指定要创建的对象类型

原型模式的优点

  1. 性能提升:直接克隆比new一个对象并初始化要快
  2. 简化创建:隐藏了对象创建的复杂性
  3. 动态配置:可以在运行时克隆对象
  4. 减少子类:不需要为每个产品创建工厂类
  5. 保存状态:可以方便地保存对象的状态快照

原型模式的缺点

  1. 克隆复杂:涉及深克隆和浅克隆的问题
  2. 循环引用:对象间存在循环引用时克隆困难
  3. 需要实现Cloneable接口:Java的克隆机制有局限性
  4. 可能过度设计:简单对象没必要用原型模式

代码示例:多种原型模式实现

1. Java浅克隆(实现Cloneable接口)

// 必须实现Cloneable接口
class Employee implements Cloneable {
    private String name;
    private int age;
    private String department;
    private List<String> skills;  // 引用类型
    
    public Employee(String name, int age, String department) {
        this.name = name;
        this.age = age;
        this.department = department;
        this.skills = new ArrayList<>();
        System.out.println("Employee构造函数被调用: " + name);
    }
    
    public void addSkill(String skill) {
        skills.add(skill);
    }
    
    // 重写clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅克隆
    }
    
    // Getter和Setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public List<String> getSkills() { return skills; }
    
    @Override
    public String toString() {
        return "Employee{name='" + name + "', age=" + age + 
               ", department='" + department + "', skills=" + skills + 
               ", hashCode=" + hashCode() + "}";
    }
}

// 客户端
public class ShallowCloneDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("=== 浅克隆示例 ===\n");
        
        // 创建原型对象
        Employee original = new Employee("张三", 30, "开发部");
        original.addSkill("Java");
        original.addSkill("Spring");
        
        System.out.println("原型对象: " + original);
        System.out.println("原型对象技能列表hashCode: " + original.getSkills().hashCode());
        
        System.out.println();
        
        // 克隆对象
        Employee cloned = (Employee) original.clone();
        System.out.println("克隆对象: " + cloned);
        System.out.println("克隆对象技能列表hashCode: " + cloned.getSkills().hashCode());
        
        System.out.println("\n=== 浅克隆的问题 ===");
        // 修改克隆对象的技能(会影响原型对象!)
        cloned.addSkill("MySQL");
        
        System.out.println("修改后原型对象: " + original);
        System.out.println("修改后克隆对象: " + cloned);
        System.out.println("原型和克隆共享同一个技能列表!");
    }
}

2. 深克隆(手动实现)

// 深克隆实现
class DeepEmployee implements Cloneable {
    private String name;
    private int age;
    private String department;
    private List<String> skills;  // 引用类型
    
    public DeepEmployee(String name, int age, String department) {
        this.name = name;
        this.age = age;
        this.department = department;
        this.skills = new ArrayList<>();
    }
    
    public void addSkill(String skill) {
        skills.add(skill);
    }
    
    // 深克隆
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 先调用父类的clone(复制基本类型)
        DeepEmployee cloned = (DeepEmployee) super.clone();
        
        // 手动克隆引用类型
        cloned.skills = new ArrayList<>(this.skills);
        
        return cloned;
    }
    
    public List<String> getSkills() { return skills; }
    
    @Override
    public String toString() {
        return "DeepEmployee{name='" + name + "', skills=" + skills + 
               ", skillsHash=" + skills.hashCode() + "}";
    }
}

// 客户端
public class DeepCloneDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("=== 深克隆示例 ===\n");
        
        DeepEmployee original = new DeepEmployee("李四", 25, "测试部");
        original.addSkill("Python");
        original.addSkill("Selenium");
        
        System.out.println("原型对象: " + original);
        
        // 深克隆
        DeepEmployee cloned = (DeepEmployee) original.clone();
        System.out.println("克隆对象: " + cloned);
        
        System.out.println("\n=== 修改克隆对象 ===");
        cloned.addSkill("JMeter");
        
        System.out.println("修改后原型对象: " + original);
        System.out.println("修改后克隆对象: " + cloned);
        System.out.println("原型和克隆技能列表独立!");
    }
}

3. 通过序列化实现深克隆

import java.io.*;

// 通过序列化实现深克隆(需要实现Serializable)
class SerializableEmployee implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private List<String> skills;
    private Address address;  // 另一个引用类型
    
    public SerializableEmployee(String name, int age) {
        this.name = name;
        this.age = age;
        this.skills = new ArrayList<>();
    }
    
    public void setAddress(Address address) {
        this.address = address;
    }
    
    public void addSkill(String skill) {
        skills.add(skill);
    }
    
    // 通过序列化实现深克隆
    public SerializableEmployee deepClone() {
        try {
            // 写入字节流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            
            // 读取字节流
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            return (SerializableEmployee) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public String toString() {
        return "SerializableEmployee{name='" + name + "', age=" + age + 
               ", skills=" + skills + ", address=" + address + 
               ", hashCode=" + hashCode() + "}";
    }
}

class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String city;
    private String street;
    
    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    
    @Override
    public String toString() {
        return "Address{city='" + city + "', street='" + street + "'}";
    }
}

// 客户端
public class SerializationCloneDemo {
    public static void main(String[] args) {
        System.out.println("=== 序列化实现深克隆 ===\n");
        
        SerializableEmployee original = new SerializableEmployee("王五", 28);
        original.addSkill("Java");
        original.addSkill("Redis");
        original.setAddress(new Address("北京", "朝阳区"));
        
        System.out.println("原型对象: " + original);
        
        // 序列化克隆
        SerializableEmployee cloned = original.deepClone();
        System.out.println("克隆对象: " + cloned);
        
        System.out.println("\n=== 验证深克隆 ===");
        cloned.addSkill("MongoDB");
        cloned.setAddress(new Address("上海", "浦东新区"));
        
        System.out.println("修改后原型对象: " + original);
        System.out.println("修改后克隆对象: " + cloned);
    }
}

实际应用场景

  1. Java标准库

    • Object.clone() 方法
    • java.util.Date.clone()
    • java.util.ArrayList.clone()
  2. 框架中使用

    • Spring中的原型作用域Bean
    • 原型模式在单元测试中创建测试对象
  3. 游戏开发

    • 创建大量敌人、子弹、特效
    • 游戏地图中的重复元素
  4. 文档系统

    • 文档模板
    • 图形绘制中的重复图形

总结

原型模式通过克隆已有对象来创建新对象,避免了复杂的初始化过程,提高了对象创建的效率。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐