原型模式(Prototype Pattern)

1 基本概念

1.1 大佬文章

设计模式是什么鬼(原型)

详解设计模式:原型模式-腾讯云开发者社区-腾讯云

1.2 知识汇总

(1)原型模式:先 new 一个实例,该实例符合需求,之后再根据这个实例为原型,重复构建新的对象;
(2)所属类型:创建型模式创建型模式
(3)作用:重复创建对象;
(4)优点:可以重复获得对象的同时保持较高的性能;

1.3 基本构成

(1)抽象原型类:抽象原型类是定义具有克隆自己的方法接口,是所有具体原型类的公共父类,可以是抽象类,也可以是接口;在 Java 中 Cloneable 接口可以看作是抽象原型类;
(2)具体原型类:根据这个类可以获得一个原型对象,并且这个类中需要重现 clone 方法,可以根据这个 clone 方法复制对象;
(3)访问类:使用具体原型类中的 clone 方法,可以不断重复的复制对象;

2 Java 中的克隆

根据现有的对象复制一个新的对象的操作就是克隆,Java 中克隆分为浅克隆和深克隆(我个人更喜欢说浅拷贝和深拷贝);浅拷贝和深拷贝的区别主要在重写 clone 方法上有所区别;但是,虽然之前有了解过拷贝的相关问题,但是发今天发现其实这里面还是有一些细节需要注意的。

2.1 拷贝的必要条件

(1)实现 Cloneable 接口:这个接口其实是一个标记接口,想要使用 clone 方法,就必须先实现这个接口,否则该类的 clone 方法是不可用的。

(2)重写 clone 方法:仅仅实现 Cloneable 接口还不能达到要求,还需要重写这个方法,否则依然不能使用 clone 方法;

2.2 浅拷贝

浅拷贝是将某个对象的所有成员属性都赋值给 clone 得到的对象;
(1)简单数据类型,如 int 等等的数据类型,直接将值拷贝过去;
(2)非简单数据类型,如引用类型的数据,是将引用的地址赋值给拷贝得到的对象,也就是说并没有新建一个成员对象,而是简单的将引用复制一遍;

package se.wangs.clonedemo.pojo;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:07
 * @description 演示浅拷贝
 */
public class Teacher {
   
    private String name;
    private String idNo;

    public String getName() {
   
        return name;
    }

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

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }
}

package se.wangs.clonedemo.pojo;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:08
 * @description 演示clone
 */
public class Student implements Cloneable{
   
    private String idNo;
    private String name;
    private Teacher teacher;

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }

    public String getName() {
   
        return name;
    }

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

    public Teacher getTeacher() {
   
        return teacher;
    }

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

    @Override
    public Student clone() {
   
        try {
   
            Student clone = (Student) super.clone();
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return clone;
        } catch (CloneNotSupportedException e) {
   
            throw new AssertionError();
        }
    }
}

package se.wangs.clonedemo;

import se.wangs.clonedemo.pojo.Student;
import se.wangs.clonedemo.pojo.Teacher;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/4 16:37
 * @description 浅拷贝
 */
public class ShallowClone {
   
    public static void main(String[] args) {
   
        Teacher teacher1 = new Teacher();
        teacher1.setIdNo("001");
        teacher1.setName("teacher_001");

        Student student1 = new Student();
        student1.setIdNo("101");
        student1.setName("student_101");
        student1.setTeacher(teacher1);

        Student cloneStudent = student1.clone();

        System.out.println("---------------------------");
        System.out.println(student1);
        System.out.println("idNo = " + student1.getIdNo());
        System.out.println("name = " + student1.getName());
        System.out.println("teacher = " + student1.getTeacher());

        System.out.println("---------------------------");
        System.out.println(cloneStudent);
        System.out.println("idNo = " + cloneStudent.getIdNo());
        System.out.println("name = " + cloneStudent.getName());
        System.out.println("teacher = " + cloneStudent.getTeacher());
    }
}

运行测试类得到的结果如下:

---------------------------
se.wangs.clonedemo.pojo.Student@41629346
idNo = 101
name = student_101
teacher = se.wangs.clonedemo.pojo.Teacher@214c265e
---------------------------
se.wangs.clonedemo.pojo.Student@448139f0
idNo = 101
name = student_101
teacher = se.wangs.clonedemo.pojo.Teacher@214c265e

说明:

  • student1:原对象
  • cloneStudent:浅拷贝得到的对象

可以看到,student 中有一个引用类型的成员变量 teacher,浅拷贝得到的 cloneStudent 中的 teacher 变量与 student1 指向的对象相同;

2.3 深拷贝

深拷贝就是会将引用类型的变量做进一步处理,clone 出的对象的引用数据类型的成员变量,会指向一个新的变量

package se.wangs.clonedemo.pojo;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:07
 * @description 演示浅拷贝
 */
public class Teacher implements Cloneable{
   
    private String name;
    private String idNo;

    public String getName() {
   
        return name;
    }

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

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }

    @Override
    public Teacher clone() {
   
        try {
   
            Teacher clone = (Teacher) super.clone();
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return clone;
        } catch (CloneNotSupportedException e) {
   
            throw new AssertionError();
        }
    }
}

package se.wangs.clonedemo.pojo;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:08
 * @description 演示clone
 */
public class Student implements Cloneable{
   
    private String idNo;
    private String name;
    private Teacher teacher;

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }

    public String getName() {
   
        return name;
    }

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

    public Teacher getTeacher() {
   
        return teacher;
    }

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

    @Override
    public Student clone() {
   
        try {
   
            Student clone = (Student) super.clone();
            // 获得原来的 teacher 对象,并clone出一个新的teacher对象
            Teacher newTeacher = clone.getTeacher().clone();
            // 重新设置teacher
            clone.setTeacher(newTeacher);
            return clone;
        } catch (CloneNotSupportedException e) {
   
            throw new AssertionError();
        }
    }
}

package se.wangs.clonedemo;

import se.wangs.clonedemo.pojo.Student;
import se.wangs.clonedemo.pojo.Teacher;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 10:09
 * @description 深拷贝
 */
public class DeepClone {
   
    public static void main(String[] args) {
   
        Teacher teacher1 = new Teacher();
        teacher1.setIdNo("001");
        teacher1.setName("teacher_001");

        Student student1 = new Student();
        student1.setIdNo("101");
        student1.setName("student_101");
        student1.setTeacher(teacher1);

        Student cloneStudent = student1.clone();

        System.out.println("---------------------------");
        System.out.println(student1);
        System.out.println("idNo = " + student1.getIdNo());
        System.out.println("name = " + student1.getName());
        System.out.println("teacher = " + student1.getTeacher());

        System.out.println("---------------------------");
        System.out.println(cloneStudent);
        System.out.println("idNo = " + cloneStudent.getIdNo());
        System.out.println("name = " + cloneStudent.getName());
        System.out.println("teacher = " + cloneStudent.getTeacher());
    }
}

运行得到

---------------------------
se.wangs.clonedemo.pojo.Student@41629346
idNo = 101
name = student_101
teacher = se.wangs.clonedemo.pojo.Teacher@214c265e
---------------------------
se.wangs.clonedemo.pojo.Student@448139f0
idNo = 101
name = student_101
teacher = se.wangs.clonedemo.pojo.Teacher@7cca494b

可以看到,得到的 teacher 对象的地址变了;

2.4 总结

浅拷贝和深拷贝的主要区别就是对引用类型的变量的处理
(1)浅拷贝:引用类型的变量拷贝前后指向同一个对象;
(2)深拷贝:引用类型的变量拷贝后指向新的对象;
(3)核心:重写 clone 方法时对引用类型变量的处理不同;
参考资料:
Java深入理解深拷贝和浅拷贝区别_java深拷贝浅拷贝-CSDN博客


3 原型设计模式

3.1 设计模式体验

(1)抽象原型类:Cloneable 接口
(2)具体原型类

package se.wangs.prototype;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:07
 * @description
 */
public class Teacher implements Cloneable{
   
    private String name;
    private String idNo;

    public String getName() {
   
        return name;
    }

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

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }

    @Override
    public Teacher clone() {
   
        try {
   
            Teacher clone = (Teacher) super.clone();
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return clone;
        } catch (CloneNotSupportedException e) {
   
            throw new AssertionError();
        }
    }
}

package se.wangs.prototype;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 9:08
 * @description 具体原型类
 */
public class Student implements Cloneable{
   
    private String idNo;
    private String name;
    private Teacher teacher;

    public String getIdNo() {
   
        return idNo;
    }

    public void setIdNo(String idNo) {
   
        this.idNo = idNo;
    }

    public String getName() {
   
        return name;
    }

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

    public Teacher getTeacher() {
   
        return teacher;
    }

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

    @Override
    public Student clone() {
   
        try {
   
            Student clone = (Student) super.clone();
            // 获得原来的 teacher 对象,并clone出一个新的teacher对象
            Teacher newTeacher = clone.getTeacher().clone();
            // 重新设置teacher
            clone.setTeacher(newTeacher);
            return clone;
        } catch (CloneNotSupportedException e) {
   
            throw new AssertionError();
        }
    }
}

(3)访问类

package se.wangs.prototype;

/**
 * -- coding: UTF-8 -- *
 *
 * @author wangs
 * @date 2023/12/6 10:30
 * @description 访问类
 */
public class Client {
   
    public static void main(String[] args) {
   
        Teacher teacher = new Teacher();
        teacher.setName("okay");
        teacher.setIdNo("0001");

        Student student = new Student();
        student.setIdNo("stu_001");
        student.setName("onesun");
        student.setTeacher(teacher);

        System.out.println("---------------------------");
        System.out.println(student);
        System.out.println("idNo = " + student.getIdNo());
        System.out.println("name = " + student.getName());
        System.out.println("teacher = " + student.getTeacher());

        for (int i = 0; i < 10; i++) {
   
            System.out.println("---------------------------");
            Student cloneStudent = student.clone();
            System.out.println(cloneStudent);
            System.out.println("idNo = " + cloneStudent.getIdNo());
            System.out.println("name = " + cloneStudent.getName());
            System.out.println("teacher = " + cloneStudent.getTeacher());
        }
    }
}

3.2 总结

(1)原型设计模式的思想就是拿到一个对象,将该对象当作一个标准,然后使用 clone 方法不断重复的去复制这个标准(原型对象),从而得到大量同类型的对象,在游戏等等场景中应用广泛。
(2)使用原型模式而不是 new 对象的优点是,节约 new 需要消耗的资源,提高系统的性能。
(3)原型模式的应用
Spring 框架对 bean 对象进行管理,而默认的 bean 对象是单例模式的,也可以使用 scope 属性指定为非单例模式,当 scope 属性为"socpe=prototype"时,就是非单例模式,其实这里使用的就是原型模式;

相关推荐

  1. 设计模式原型模式

    2023-12-07 07:44:05       63 阅读
  2. 设计模式——原型模式

    2023-12-07 07:44:05       49 阅读
  3. 设计模式-原型模式

    2023-12-07 07:44:05       58 阅读
  4. 设计模式-原型模式

    2023-12-07 07:44:05       50 阅读
  5. 设计模式-原型模式

    2023-12-07 07:44:05       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-07 07:44:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-07 07:44:05       101 阅读
  3. 在Django里面运行非项目文件

    2023-12-07 07:44:05       82 阅读
  4. Python语言-面向对象

    2023-12-07 07:44:05       91 阅读

热门阅读

  1. python跑ncnn(验证模型是否转换成功)

    2023-12-07 07:44:05       56 阅读
  2. 排序算法之二:冒泡排序

    2023-12-07 07:44:05       71 阅读
  3. 泛洪填充(Flood Fill)

    2023-12-07 07:44:05       55 阅读
  4. 经典C语言程序之 编程】详细解析及示例代码

    2023-12-07 07:44:05       59 阅读
  5. 机器学习---pySpark案例

    2023-12-07 07:44:05       59 阅读
  6. C# 未处理System.InvalidOperationException HResult=-2146233079

    2023-12-07 07:44:05       46 阅读
  7. mysql配置所有人可连接_mysql配置允许外界连接

    2023-12-07 07:44:05       52 阅读
  8. Git 克隆子目录

    2023-12-07 07:44:05       57 阅读
  9. mysql的show full processlist状态详解

    2023-12-07 07:44:05       47 阅读
  10. leetcode707.设计链表

    2023-12-07 07:44:05       59 阅读
  11. 晶闸管的管脚及好坏判别

    2023-12-07 07:44:05       60 阅读