• 22.原型模式


    1.什么是原型模式?

    原型模式是一种创建型模式,是我们能够复制已有对象而又无需使代码依赖他们所属类的一种设计模式。

    1.1原型模式解决的问题

    首先抛出一个问题:
    如果我们有一个类型为Student的对象,并希望生成与其完全相同的克隆对象,我们该如何实现呢?
    在这里插入图片描述

    如果没有原型模式,我们可能的做法是:
    首先,新建一个这个类的对象,然后遍历目标对象中的所有成员变量,并将成员变量的值复制到新对象中。(直接复制) 实例代码如下:

    public static void main(String[] args) {
        Student student = new Student();
        student.setName("黄四郎");
        student.setNum(1);
        student.setAge(40);
        student.setWeight(180);
        System.out.println(student.toString());
    
        Student student1 = new Student();
        System.out.println(student1.toString());
        student1.setName(student.getName());
        student1.setNum(student.getNum());
        student1.setAge(student.getAge());
        student1.setWeight(student.getWeight());
        System.out.println(student1);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    这样做在一些情况下可以满足我们的需求,但是有一种情况,我们可能无能为力,那就是我们如何复制目标对象的私有变量。(在不用反射和不提供共有获取方法的情况下) 因为私有变量在对象本身以外是不可见的。
    上面我们采用的就是在定义变量的时候提供了公有get方法,所以能顺利复制。

    直接复制还有另外一个问题,那就是我们必须知道对象所属的类才能创建复制对象,所以代码必须依赖该类。 即使我们可以接收额外的依赖性,那还有一个问题: 有时我们只知道对象所属的接口,但不知道其所属的具体类。 这时我们又该如何去做?

    答案就是使用原型模式

    1.2 解决方案: 原型模式

    原型模式将对象clone过程委托给被clone的目标对象。也就是说,目标对象的clone过程是在对象内部完成的。
    原型模式为所有支持clone的对象声明了一个通用接口( 通常情况下,这样的接口中仅包含一个clone方法),该接口让我们能够clone对象,同时又无需将代码和对象所属类耦合。
    UML如下:
    在这里插入图片描述

    所有类对clone的方法实现非常类似,该方法首先会创建一个当前类的对象,然后将原始对象所有成员变量的值复制到新建的类中。(包括私有变量哦) 支持clone的对象我们称之为原型。
    下面我们进行实践,将上面的Student类改为原型模式
    在这里插入图片描述

    其中clone的实现是:

    @Override
    public Student clone() {
        Student student = new Student();
        student.name = this.name;
        student.age = this.age;
        student.num = this.num;
        student.weight = this.weight;
        return student;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样做之后,我们就可以用下面的方式进行克隆:

    public static void main(String[] args) {
        Student student = new Student();
        student.setName("黄四郎");
        student.setNum(1);
        student.setAge(40);
        student.setWeight(180);
        System.out.println(student.toString());
    
        Student student1 =  student.clone();
        System.out.println(student1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    当我们的对象有几十个变量和几百种类型时,对其进行clone甚至可以代替子类的构造。
    运作方式如下: 创建一系列不同类型对象并用不同的方式进行配置。如果所需对象与预先配置的对象相同,那么我们只需要clone原型即可,无需新建一个对象。

    代码地址如下:(其中first包中对应本博客的内容,second是一位大神写的原型模式应用实例)
    设计模式/src/main/java/PrototypePattern · 严家豆/Head first 设计模式学习 - 码云 - 开源中国 (gitee.com)

    2.原型模式的应用

    String的特殊性在于:
    因为他为引用型,而且他指向的值为常量,克隆出来的对象改变他的值。实际上是改变了克隆出来对象String类型成员的指向,不会影响被克隆对象的值及其指向。

    2.1 Object中的clone

    在JDK中提供了接口Cloneable,这时一个标记接口,和Object同包, Object中提供了一个

    protected native Object clone() throws CloneNotSupportedException;
    
    • 1

    它是用来对对象进行克隆的方法。
    这就是为什么JDK中如果想要一个类具有克隆能力就必须实现Cloneable接口的原因。注意: 而且如果我们如果不对clone方法进行重写的话,我们使用clone方法得到的对象是浅克隆的。

    关于浅克隆和深克隆的差别,请看下面的示例:

    class RunoobTest implements Cloneable {
    
        // 声明变量
        String name;
        int likes;
        RunoobTest runoobTest;
    
        public static void main(String[] args) {
    
            // 创建对象
            RunoobTest obj1 = new RunoobTest();
            RunoobTest inObj = new RunoobTest();
            inObj.name = "内部引用对象";
            inObj.likes=222;
            obj1.runoobTest = inObj;
    
            // 初始化变量
            obj1.name = "Runoob";
            obj1.likes = 111;
    
            // 打印输出
            System.out.println(obj1.name); // Runoob
            System.out.println(obj1.likes); // 111
            System.out.println(obj1.runoobTest.name);
            System.out.println(obj1.runoobTest.likes);
    
            try {
                // 创建 obj1 的拷贝
                RunoobTest obj2 = (RunoobTest) obj1.clone();
    
                // 使用 obj2 输出变量
                System.out.println(obj2.name); // Runoob
                System.out.println(obj2.likes); // 111
                System.out.println(obj2.runoobTest.name);
                System.out.println(obj2.runoobTest.likes);
                obj2.name = new String("copy2");//这个操作不会影响obj1的内容,原因是String是final类型的,不可变的。
                obj2.runoobTest.name = "copy";//这个操作会印象obj1的内容
            } catch (Exception e) {
                System.out.println(e);
            }
    
            System.out.println(obj1.name); // Runoob
            System.out.println(obj1.likes); // 111
            System.out.println(obj1.runoobTest.name);
            System.out.println(obj1.runoobTest.likes);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述

    最后本文的内容主要吸收总结与一个很好的设计模式学习网站:
    Java 生成器模式讲解和代码示例 (refactoringguru.cn) 推荐大家访问学习

  • 相关阅读:
    华为eNSP配置专题-IPSec的配置
    Javascript——JS常用的方法(二)常用字符串方法
    危机重重下的企业发展,数字化转型到底是不是企业未来救星
    java基于ssm的汽车维修工时费快速估价系统
    【AI视野·今日Robot 机器人论文速览 第四十一期】Tue, 26 Sep 2023
    视觉SLAM十四讲(高翔版本),ch1-2章节部分笔记
    海康、大华等IPC解码上墙,PC上平台同时查看方案
    vscode远程连接开发机失败/解决方案大合集
    第34节——useImperativeHandle
    网安朝阳·西门子白帽黑客大赛 | 聚焦实战攻防竞赛 促进网安人才发展
  • 原文地址:https://blog.csdn.net/c1776167012/article/details/126314628