1. 设计模式之原型模式:自己实现自己的对象拷贝逻辑

1.1. 介绍

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。通过已有的一个原型对象,快速生成与原型对象相同的实例。

1.2. 优缺点及建议

优点

  1. 性能高效:通过复制现有对象避免重复初始化,适合创建成本高的对象(如数据库连接、复杂计算实例)。
  2. 动态配置灵活:基于模板对象快速生成变体,仅需修改差异属性(如游戏中的敌人类型、配置模板)。
  3. 减少子类依赖:通过克隆替代继承,避免工厂子类膨胀(如不同风格的UI组件)。
  4. 隐藏构造细节:客户端无需知道对象的具体构造逻辑,直接调用clone()即可。

缺点

  1. 深拷贝实现复杂:需手动处理引用类型字段的复制,否则浅拷贝会导致对象共享(如修改克隆对象的List会影响原对象)。
  2. 破坏封装性:如果实现Cloneable接口并重写clone()方法(Java中clone()protected,违反面向对象设计原则)。
  3. 循环引用问题:若对象存在相互引用(如A→B→A),深拷贝需额外处理(如序列化或深度遍历)。
  4. 不适合简单对象:若对象构造成本低,直接new比克隆更直观(如Point(x, y))。

使用建议

  1. 资源密集型对象:如线程池、数据库连接、大型游戏角色(初始化耗时长时优先使用克隆)。
  2. 动态配置场景:基于模板对象生成变体(如用户权限配置、文档模板)。
  3. 避免工厂类爆炸:当对象变体过多时,用克隆替代工厂子类(如不同风格的按钮、图标)。
  4. 替代方案考虑
    • 若深拷贝太复杂,可用 复制构造器new User(originalUser))或 静态工厂方法User.copy(original))。
    • 对于不可变对象(如String),直接共享实例即可,无需克隆。

不推荐场景

  • 简单POJO(如User仅有idname字段)。
  • 高频创建的小对象(克隆的轻微开销可能成为瓶颈)。
  • 不可变对象(如枚举、Integer)。

1.3. 实现及相关代码

假设有一个对象需要生成一个数据完全一样,但是是新的对象。

1.3.1. 没使用设计模式方式

接口及数据对象

public static class  Component {
    private String name;

    public Component(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Component [name=" + name + "]";
    }
}

public static class Product {
    private String name;
    private Component component;

    public Product(String name, Component component) {
        super();
        this.name = name;
        this.component = component;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Component getComponent() {
        return component;
    }
    public void setComponent(Component component) {
        this.component = component;
    }

    @Override
    public String toString() {
        return "Product [name=" + name + ", component=" + component + "]";
    }
}

调用方法

public static void main(String[] args) {
    Product product = new Product("测试产品", new Component("测试组件"));
    // 手动来拷贝
    Product copyProduct = new Product(product.getName(), product.getComponent());
    System.out.println(copyProduct);
}

可能会遇到的问题

代码的拷贝逻辑,是每个要拷贝的调用方自己来实现的,对象复杂的话,复杂逻辑很多,造成逻辑代码复杂。

相同的拷贝逻辑会分散在很多不同的地方,如果拷贝逻辑改变了,多个调用的地方都要修改代码,可维护性、可扩展性,很差。

1.3.2. 使用设计模式方式

使用原型模式,在类里实现一个clone()方法,自己拷贝自己。很多地方要克隆这个对象,不要自己维护克隆的逻辑,即使克隆逻辑修改了,只要在clone()方法里面修改即可。

拷贝的时候,注意两个概念,浅拷贝深拷贝

特性 浅拷贝 深拷贝
定义 仅复制对象本身和基本类型字段,引用类型字段共享原对象的地址。 完全复制对象及其所有嵌套对象,引用类型字段也生成新实例。
内存表现 原对象和拷贝对象的部分字段指向同一内存地址(引用类型)。 原对象和拷贝对象的所有字段(包括嵌套对象)均独立占用内存。
修改影响 修改拷贝对象的引用类型字段会影响原对象。 修改拷贝对象的任何字段均不影响原对象。
实现方式 默认的Object.clone()BeanUtils.copyProperties 手动递归复制、序列化/反序列化、工具类(如Apache Commons Lang的SerializationUtils

接口及数据对象

public static class  Component {
    private String name;

    public Component(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Component [name=" + name + "]";
    }

    @Override
    protected Component clone() {
        return new Component(getName());
    }
}

public static class Product {
    private String name;
    private Component component;

    public Product(String name, Component component) {
        super();
        this.name = name;
        this.component = component;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Component getComponent() {
        return component;
    }
    public void setComponent(Component component) {
        this.component = component;
    }

    @Override
    public String toString() {
        return "Product [name=" + name + ", component=" + component + "]";
    }

    @Override
    protected Product clone() {
        // 浅拷贝,仅仅简单的对当前所有的变量进行一个拷贝
//        return new Product(getName(), getComponent());

        // 深拷贝,递归对自己引用的对象也进行拷贝
        return new Product(getName(), getComponent().clone());
    }
}

调用方法

public static void main(String[] args) {
    Product product = new Product("测试产品", new Component("测试组件"));
    // 很多地方要克隆这个对象,不要自己维护克隆的逻辑,即使克隆逻辑修改了,只要在clone()方法里面修改
    Product copyProduct = product.clone();
    System.out.println(copyProduct);
}

results matching ""

    No results matching ""