JAVA:深入理解原型模式构建可复用对象的关键技术

请关注微信公众号:拾荒的小海螺

1、简述

在软件开发中,有时候我们需要创建许多相似但不完全相同的对象,这时候使用原型模式就显得非常有用。原型模式是一种创建型设计模式,它允许我们通过复制现有对象来创建新对象,而无需从头开始构建。本文将深入探讨 Java 中的原型模式,解释其原理、用法以及常见的应用场景。

在这里插入图片描述

2、原理

原型模式的核心思想是通过克隆(拷贝)现有对象来创建新的对象。这种克隆可以分为浅克隆和深克隆两种形式:

  • 浅克隆(Shallow Clone):只复制对象本身,而不复制对象的引用类型属性。即新对象和原对象共享引用类型属性。
  • 深克隆(Deep Clone):复制对象本身以及对象的引用类型属性,创建全新的对象,新对象和原对象的引用类型属性不共享。

Java 中实现原型模式的方式主要有两种:通过实现 Cloneable 接口和通过序列化。

3、浅拷贝

Java 提供了 Cloneable 接口,该接口表示一个类支持被复制(克隆)。如果一个类实现了 Cloneable 接口,那么它就可以使用 clone() 方法进行对象的克隆。下面是一个简单的示例:

public class Prototype implements Cloneable {
    private String data;
    /**
     * 引用Teacher 对象
     */
    private Teacher teacher;

    public Prototype(String data) {
        this.data = data;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}

引用的Teacher对象实例:

public class Teacher {
    private String data;

    public Teacher(String data) {
        this.data = data;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

通过使用main函数示例输出:

public class CloneMain {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("Teacher Data");
        Prototype prototype = new Prototype("Original Data");
        teacher.setData("Teacher Data");
        prototype.setTeacher(teacher);
        
        Prototype clonedPrototype = prototype.clone();
        clonedPrototype.setData("Original Data1");
        clonedPrototype.getTeacher().setData("Teacher Data2");

        System.out.println("原来对象:" + prototype.getData()); // 输出:Original Data
        System.out.println("引用原来对象:" + prototype.getTeacher().getData()); // 输出:Teacher Data2

        System.out.println("拷贝对象:" + clonedPrototype.getData()); // 输出:Original Data1
        System.out.println("引用拷贝对象:" + clonedPrototype.getTeacher().getData()); // 输出:Teacher Data2
    }
}

通过执行控制台输出结果:

原来对象:Original Data
引用原来对象:Teacher Data2
拷贝对象:Original Data1
引用拷贝对象:Teacher Data2

观察以上运行结果,我们可以发现:在我们给Prototype 拷贝对象 修改老师的时候,Prototype 原型的老师也跟着被修改了。这就是浅拷贝带来的问题。

4、深拷贝

另一种实现深克隆的方法是通过序列化和反序列化。通过将对象写入输出流,然后再从输入流中读取对象,可以创建一个新的对象,这个过程中会创建对象的副本。下面是一个示例:

4.1 使用序列化和反序列化

通过将对象写入输出流,然后再从输入流中读取对象,可以创建一个新的对象,这个过程中会创建对象的副本。

import java.io.*;

public class DeepPrototype implements Serializable {
    private String data;
    /**
     * 引用Teacher 对象
     */
    private Teacher teacher;

    public DeepPrototype(String data) {
        this.data = data;
    }



    public DeepPrototype deepClone() throws IOException, ClassNotFoundException {
        // 写入输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(this);

        // 从输入流中读取对象
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        return (DeepPrototype) objectInputStream.readObject();
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}

引用的Teacher对象实例也要实现序列化Serializable:

public class Teacher implements Serializable {
    private String data;

    public Teacher(String data) {
        this.data = data;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

通过使用main函数示例输出:

public class CloneMain {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Teacher teacher = new Teacher("Teacher Data");
        DeepPrototype prototype = new DeepPrototype("Original Data");
        teacher.setData("Teacher Data");
        prototype.setTeacher(teacher);

        DeepPrototype clonedPrototype = prototype.deepClone();
        clonedPrototype.setData("Original Data1");
        clonedPrototype.getTeacher().setData("Teacher Data2");

        System.out.println("原来对象:" + prototype.getData()); // 输出:Original Data
        System.out.println("引用原来对象:" + prototype.getTeacher().getData()); // 输出:Teacher Data

        System.out.println("拷贝对象:" + clonedPrototype.getData()); // 输出:Original Data2
        System.out.println("引用拷贝对象:" + clonedPrototype.getTeacher().getData()); // 输出:Teacher Data2
    }
}

通过执行控制台输出结果:

原来对象:Original Data
引用原来对象:Teacher Data
拷贝对象:Original Data1
引用拷贝对象:Teacher Data2
4.2 使用 JSON 序列化和反序列化

将对象转换为 JSON 格式的字符串,然后再从字符串中反序列化出对象。这种方法需要使用一个 JSON 库,如 Jackson、Gson 等。以下是一个示例:

import com.fasterxml.jackson.databind.ObjectMapper;

public class DeepCopyUtils {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    public static <T> T deepCopy(T obj, Class<T> clazz) throws IOException {
        String json = objectMapper.writeValueAsString(obj);
        return objectMapper.readValue(json, clazz);
    }
}

4.3 使用 BeanUtils

Apache Commons BeanUtils 库提供了 BeanUtils.cloneBean() 方法,可以复制对象的属性到新对象中。这种方法需要确保对象的属性都是可序列化的。以下是一个示例:

import org.apache.commons.beanutils.BeanUtils;

public class DeepCopyUtils {
    public static <T> T deepCopy(T obj) throws ReflectiveOperationException {
        return (T) BeanUtils.cloneBean(obj);
    }
}

你可以使用 DeepCopyUtils.deepCopy() 方法来实现深拷贝。

5、应用场景

原型模式在开源框架中的应用也非常广泛。例如 Spring 中,@Service 默认都是单例的。用了私有全局变量,若不想影响下次注入或每次上下文获取 bean,就需要用到原型模式,我们可以通过以下注解来实现,@Scope(“prototype”)。

  • 当对象的创建成本较高,但需要频繁创建相似对象时,可以使用原型模式。
  • 当需要避免构造函数重复调用,而又需要获得新对象时,可以使用原型模式。
  • 当对象的结构比较复杂,无法直接使用 new 关键字创建对象时,可以使用原型模式。

6、总结

原型模式是一种创建型设计模式,通过复制现有对象来创建新对象,能够有效地减少对象的创建成本,提高代码的可复用性和可维护性。在 Java 中,可以通过实现 Cloneable 接口或者序列化来实现原型模式。通过深入理解原型模式的原理和用法,我们可以更好地应用它解决实际的软件设计和开发问题。

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾荒的小海螺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值