自学教程 学编程从这里开始

原型模式

《原型模式》|设计模式系列第06篇

系列:创建型模式 · 原型模式(Prototype Pattern)
关键词:对象克隆、浅拷贝、深拷贝、原型实例、减少new开销

一、什么是原型模式

原型模式(Prototype Pattern):用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

属于创建型设计模式。

简单理解:
不再通过 new 新建对象,而是拿一个现成对象当模板,直接复制克隆一份。适合对象创建成本高的场景,规避构造函数执行开销。

核心意图
降低对象创建成本;复用已有对象的数据,快速生成新实例。

适用场景

  1. 对象创建开销大(初始化耗时、读取数据库/文件、网络加载)
  2. 需要大量相似对象,对象之间仅有少量字段差异
  3. 不想暴露复杂对象创建逻辑给客户端
  4. 动态获取对象类型,运行时复制实例

⚠️ 缺点预警:需要合理处理浅拷贝与深拷贝;对象包含循环引用时克隆逻辑复杂。

二、UML结构图

角色划分:

  1. Prototype(抽象原型):声明克隆方法,一般提供 clone()
  2. ConcretePrototype(具体原型):实现克隆方法,复制自身生成新对象
  3. Client(客户端):获取原型对象,调用克隆方法生成新实例

三、Java 完整示例

Java 原生提供 Cloneable 标记接口,重写 clone() 方法实现原型。

注意:Object.clone() 默认是浅拷贝。

1. 具体原型类(浅拷贝示例)

public class User implements Cloneable {
    private String username;
    private int age;
    private Address address;

    public User(String username, int age, Address address) {
        this.username = username;
        this.age = age;
        this.address = address;
    }

    @Override
    protected User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }

    // getter & setter
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

// 引用类型成员
class Address {
    private String city;

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

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

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

2. 客户端调用

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("杭州");
        User prototype = new User("张三", 20, address);

        // 克隆原型对象
        User cloneUser = prototype.clone();
        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneUser);

        // 浅拷贝:引用对象地址是同一个
        cloneUser.getAddress().setCity("上海");
        System.out.println("修改克隆对象地址后,原型对象:" + prototype);
    }
}

运行结果可以看到:修改克隆对象里的引用类型 Address,原型对象的地址也跟着变化,这就是浅拷贝。

3. 改造为深拷贝

修改 User 的 clone 方法,把引用对象也单独克隆一份:

@Override
protected User clone() throws CloneNotSupportedException {
    User user = (User) super.clone();
    // 引用类型单独拷贝
    user.address = new Address(address.getCity());
    return user;
}

此时修改克隆对象的 Address,不会影响原型对象。

四、浅拷贝 vs 深拷贝

  • 浅拷贝:复制基本数据类型;引用类型只复制对象引用地址,共用同一个引用对象。
  • 深拷贝:基本类型、引用类型全部新建独立对象,新旧对象完全隔离,互不干扰。

实现深拷贝可选方案:

  1. 手动在 clone 方法中递归拷贝引用对象
  2. 对象序列化/反序列化(Jackson,Java原生序列化)

五、优缺点总结

✅ 优点

  1. 克隆比 new 性能更高,适合创建开销大的对象
  2. 简化对象创建流程,客户端直接复制原型
  3. 动态新增/修改原型,灵活生成相似对象

❌ 缺点

  1. 需要编写克隆逻辑,尤其深拷贝实现繁琐
  2. 存在循环引用时,克隆逻辑很难处理
  3. 每一个原型类都需要支持克隆,需要改造原有代码

六、原型模式与其他创建型模式对比

模式创建方式核心特点
工厂/建造者new 创建对象侧重于对象组装与实例化
原型模式克隆复制对象基于已有实例复制,规避昂贵构造

七、框架源码应用

  • JDK java.util.ArrayList.clone()
  • Spring 中原型 Bean(scope=”prototype”),每次获取返回全新实例,思想源自原型模式
  • Apache Commons 工具类提供原型克隆工具

八、小结

原型模式核心:以现有对象作为模板进行克隆复制。
重点区分浅拷贝与深拷贝;对象内部存在引用类型时,一定要评估拷贝方式带来的副作用。
适合对象初始化成本高,大量相似实例场景。

标签:

0 条笔记