• 设计模式之原型模式--超越实例化的魔法,从复制到创造的无限可能


    概述

    什么是原型模式

        原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需依赖显式的实例化过程。使用原型模式,我们可以在运行时动态地创建对象,并且能够轻松地生成对象的副本。

    浅拷贝和深拷贝

        在原型模式中,复制对象的方式通常可以分为浅拷贝和深拷贝两种。

    • 浅拷贝:浅拷贝创建一个新对象,该对象与原始对象共享一些属性。这意味着新对象中的引用类型属性仍然引用原始对象中相同的对象,对引用类型属性的更改会影响到原始对象和所有浅拷贝的对象。
    • 深拷贝:深拷贝创建一个新对象,该对象与原始对象完全独立。这意味着新对象中的引用类型属性指向新的对象,对引用类型属性的更改不会影响原始对象或其他深拷贝的对象。
      在选择浅拷贝还是深拷贝时,需要根据具体的需求和对象结构来决定。

    类图

    在这里插入图片描述

    原型中的主要角色

    • 原型(Prototype):原型是一个抽象类或接口,定义了可以复制自己的方法 clone()。具体的原型对象需要实现这个方法来进行对象的复制。

    • 具体原型(Concrete Prototype):具体原型是实现了原型接口的具体类。它通过实现 clone() 方法来复制自身,并生成一个新的对象,保持与原始对象相似的状态。

    • 客户端(Client):客户端是使用原型对象的地方。它通过请求原型对象来创建新的对象实例,而无需直接调用构造函数进行实例化

    工作流程

        在原型模式中,客户端通过以下步骤使用原型对象:

    1. 创建一个原型对象的实例。 通过调用原型对象的 clone() 方法复制自己,从而得到一个新的对象。

    2. 对新对象进行进一步的定制和修改,使其符合需求。
      在这里插入图片描述

    代码衍化过程

    业务需求:打印简历

    初版

    业务:打印个人简历,包括个人信息,工作经历

    //简历类
    public class Resume {
        private String name;
        private String sex;
        private String age;
        private String timeArea;
        private String company;
    
        public Resume(String name){
            this.name=name;
        }
        //设置个人信息
        public void setPersonalInfo(String sex,String age){
            this.age=age;
            this.sex=sex;
        }
    
        //设置工作经历
        public void setWorkExperience(String timeArea,String company){
            this.timeArea=timeArea;
            this.company=company;
    
        }
        //展示简历
        public void display(){
            System.out.println(this.name+" " +this.sex+" "+this.age);
            System.out.println("工作经历"+this.timeArea+" "+this.company);
        }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            Resume resume1=new Resume("张三");
            resume1.setPersonalInfo("男","22");
            resume1.setWorkExperience("2020-2023","XX公司");
            resume1.display();
    
            Resume resume2=new Resume("张三");
            resume2.setPersonalInfo("男","22");
            resume2.setWorkExperience("2020-2023","XX公司");
            resume2.display();
    
            Resume resume3=new Resume("张三");
            resume3.setPersonalInfo("男","22");
            resume3.setWorkExperience("2020-2023","XX公司");
            resume3.display();
        }
    }
    
    }
    
    • 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
    • 49
    • 50

        一个简历类,在客户端中通过实例化简历类传入相同的信息来实现打印多份简历
        问题:客户端麻烦,打印几份简历客户端就是要实例化几次,而且调整简历内容,每个都要调整。
        引入原型模式

    原型模式基本代码

    //原型
    abstract class Prototype implements Cloneable{
        private String id;
        public Prototype(String id){
            this.id=id;
    
        }
        public String getId() {
            return id;
        }
    
       //原型的关键方法
        public Object clone(){
            Object object=null;
            try {
                object=super.clone();
            } catch (CloneNotSupportedException e) {
                System.err.println("clone异常");
            }
            return object;
        }
    }
    
    //具体原型
    public class ConcreteProtype extends Prototype{
        public ConcreteProtype(String id){
            super(id);
        }
    }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            ConcreteProtype p1=new ConcreteProtype("编号123456");
            System.out.println("原型ID:"+p1.getId());
    
            ConcreteProtype c1=(ConcreteProtype)p1.clone();
            System.out.println("克隆ID:"+c1.getId());
        }
    
    }
    
    
    • 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

    运行结果
    在这里插入图片描述
        这里不用实例化ConcreteProtype ,直接使用克隆对象,对于Java而言,Prototype 是用不上的,因为Java提供了Cloneable接口,其中唯一的方法clone(),使用时直接实现这个接口就可以完成原型模式了。

    简历的原型实现

    //简历类
    public class Resume implements Cloneable{
        private String name;
        private String sex;
        private String age;
        private String timeArea;
        private String company;
    
        public Resume(String name){
            this.name=name;
        }
        //设置个人信息
        public void setPersonalInfo(String sex,String age){
            this.age=age;
            this.sex=sex;
        }
    
        //设置工作经历
        public void setWorkExperience(String timeArea,String company){
            this.timeArea=timeArea;
            this.company=company;
    
        }
        //展示简历
        public void display(){
            System.out.println(this.name+" " +this.sex+" "+this.age);
            System.out.println("工作经历"+this.timeArea+" "+this.company);
        }
    
        public Resume Clone(){
            Resume object=null;
            try {
                object=(Resume) super.clone();
            } catch (CloneNotSupportedException e) {
               System.err.println("clone异常");
            }
            return object;
        }
    }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            Resume resume1=new Resume("大鸟");
            resume1.setPersonalInfo("男","23");
            resume1.setWorkExperience("2020-2023","Xx公司");
    
            Resume resume2=resume1.Clone();
            resume2.setWorkExperience("2022-2023","YY公司");
    
            Resume resume3=resume1.Clone();
            resume3.setPersonalInfo("男","24");
    
            resume1.display();
            resume2.display();
            resume3.display();
    
        }
    }
    
    
    • 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
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

        在上面的代码中,Resume 类实现了 Cloneable 接口,表明该类可以被克隆。

        Clone() 方法重写了 Object 类的 clone() 方法,通过调用 super.clone() 实现浅拷贝,并返回一个克隆后的新对象。

        客户端的代码就简单很多,要是想改某份简历,只需要对这份简历做一定的修改就可以了,不会影响到其他简历,而且提高了效率,不用重新初始化对象,而是动态地获得对象运行时的状态。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。

    浅复制

    //工作经历类
    public class WorkExperience {
        //工作时间范围
        private String timeArea;
    
        public String getTimeArea() {
            return timeArea;
        }
    
        public void setTimeArea(String timeArea) {
            this.timeArea = timeArea;
        }
    
        //所在公司
        private String company;
    
        public String getCompany() {
            return company;
        }
    
        public void setCompany(String company) {
            this.company = company;
        }
    }
    
    //简历类
    public class Resume implements Cloneable {
        private String name;
        private String sex;
        private String age;
        private WorkExperience work;//声明一个工作经历对象
        public Resume(String name){
            this.name=name;
            this.work=new WorkExperience();//实例化一个工作 经历对象
        }
    
        public void setPersonalInfo(String sex,String age){
            this.age=age;
            this.sex=sex;
        }
    
        //设置工作经历
        public void setWorkExperience(String timeArea,String company){
            this.work.setTimeArea(timeArea);
            this.work.setCompany(company);;
    
        }
        //展示简历
        public void display(){
            System.out.println(this.name+" " +this.sex+" "+this.age);
            System.out.println("工作经历"+this.work.getTimeArea()+" " +this.work.getCompany());
        }
    
        public Resume clone(){
            Resume object=null;
            try {
                object=(Resume) super.clone();
            } catch (CloneNotSupportedException e) {
                System.err.println("clone异常");
            }
            return object;
        }
    
    
    
    }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            Resume resume1=new Resume("张三");
            resume1.setPersonalInfo("男","30");
            resume1.setWorkExperience("2000-2010","Xx公司");
    
            Resume resume2=resume1.clone();
            resume2.setWorkExperience("2001-2011","YY集团");
    
            Resume resume3=resume1.clone();
            resume3.setPersonalInfo("女","35");
            resume3.setWorkExperience("2020-2023","ZZ公司");
    
            resume1.display();
            resume2.display();
            resume3.display();
    
        }
    }
    
    • 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
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    运行结果
    在这里插入图片描述
        为什么会出现这种情况,引用对象好像覆盖了前面的对象,这个需要了解一下clone()方法。

        Object 类的 clone() 方法是一个受保护的本地方法。代码中的super.clone() 方法内部的实现通过调用本地方法来完成对象的浅拷贝。

        实际上,Object 类的 clone() 方法的实现有一定的约束条件:

        首先,clone() 方法只能被实现了 Cloneable 接口的类调用。如果没有实现 Cloneable 接口,调用 clone() 方法将抛出 CloneNotSupportedException 异常。

        其次,clone() 方法返回的是当前对象的浅拷贝,即新对象和原始对象共享相同的数据引用

        这意味着,只有基本类型的字段会被复制,而对于非基本类型(如数组、集合、对象等),只是复制了引用而不是创建新的对象。

        由于浅拷贝只是复制了引用而不是创建新的对象,所以在使用 super.clone() 方法时需要小心处理可变对象和引用类型的字段。如果希望进行深拷贝,需要在 clone() 方法中显式地处理这些字段,以确保每个对象都是独立的副本。

    深拷贝

    //工作经验类也实现cloneable接口
    public class WorkExperience implements Cloneable{
        private String timeArea;
    
        public String getTimeArea() {
            return timeArea;
        }
    
        public void setTimeArea(String timeArea) {
            this.timeArea = timeArea;
        }
    
        //所在公司
        private String company;
    
        public String getCompany() {
            return company;
        }
    
        public void setCompany(String company) {
            this.company = company;
        }
    
        public WorkExperience clone(){
           WorkExperience object=null;
            try {
                object=(WorkExperience) super.clone();
            } catch (CloneNotSupportedException e) {
                System.err.println("clone异常");
            }
            return object;
        }
    }
    
    //简历类
    public class Resume implements Cloneable{
        private String name;
        private String sex;
        private String age;
        private WorkExperience work;//声明一个工作经历对象
        public Resume(String name){
            this.name=name;
            this.work=new WorkExperience();//实例化一个工作 经历对象
        }
    
        public void setPersonalInfo(String sex,String age){
            this.age=age;
            this.sex=sex;
        }
    
        //设置工作经历
        public void setWorkExperience(String timeArea,String company){
            this.work.setTimeArea(timeArea);
            this.work.setCompany(company);;
    
        }
        //展示简历
        public void display(){
            System.out.println(this.name+" " +this.sex+" "+this.age);
            System.out.println("工作经历"+this.work.getTimeArea()+" " +this.work.getCompany());
        }
    
        public Resume clone(){
         Resume object=null;
            try {
                object=(Resume) super.clone();
                this.work= this.work.clone();//对克隆对象里的引用也进行克隆,就达到了深复制的作用
    
            } catch (CloneNotSupportedException e) {
                System.err.println("clone异常");
            }
            return object;
        }
    
    //客户端不变
    
    }
    
    • 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
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    实现结果
    在这里插入图片描述
    这样就达到了预期的效果。

    原型模式的优点

    • 动态创建对象:原型模式允许在运行时动态地创建对象,而无需显式的实例化过程。这使得我们能够根据需要创建对象的副本,而不必依赖固定的类层次结构。

    • 简化对象创建过程:通过复制现有对象,我们可以更轻松地创建新对象。这比手动设置对象的初始状态要简单得多,尤其当对象的创建过程非常复杂时。

    • 减少资源消耗:与实例化相比,原型模式的对象复制过程通常更高效。它避免了执行复杂的初始化逻辑和与构造函数相关的资源消耗。

    原型模式的应用场景

    • 当系统中的对象数量很大,但创建和初始化对象的过程很复杂时,原型模式可以显著简化对象的创建过程。
    • 当需要创建对象的副本,而无需暴露对象的创建细节时,可以使用原型模式。
    • 当需要避免与对象的产生和初始化过程相关的资源消耗时,原型模式是一个很好的选择。

    原型模式的创新

        原型模式在对象创建的过程中,通过复制现有对象来创建新对象,从而避免了显式的实例化过程。这种动态创建对象的方式带来了一些创新和优势:

        简化对象创建:原型模式使得创建新对象变得简单直观,通过在现有对象的基础上进行复制,避免了繁琐的初始化过程和构造函数的调用。相比传统的实例化方式,原型模式提供了更加简洁、灵活的对象创建方式。

        动态性和扩展性:原型模式允许在运行时动态地创建对象的副本。这意味着可以根据需要创建不同类型的对象,并且可以随时修改或增加现有对象的属性和行为。这种动态性和扩展性使得原型模式在设计阶段更具灵活性和适应性。

        减少资源消耗:相对于传统的实例化方式,原型模式的对象复制过程通常更高效。它避免了执行复杂的初始化逻辑和与构造函数相关的资源消耗。通过原型模式,可以复用已有对象的状态,减少了重复创建对象的开销。

        保护对象创建细节:原型模式将对象的创建细节封装在原型对象内部,对客户端隐藏了具体的创建过程。这样,客户端无需了解对象的具体创建逻辑,仅需通过原型对象进行复制即可获得新对象。这种封装性保护了对象的创建细节,提高了代码的可维护性和安全性。

    总结

        原型模式允许通过复制现有对象来创建新对象,而无需显式的实例化过程。它提供了一种动态创建对象的方式,并且可以简化对象的创建过程。通过浅拷贝和深拷贝的选择,可以控制对象副本的共享程度。

  • 相关阅读:
    ansible配置文件介绍
    HTTP/2的三大改进:头部压缩、多路复用和服务器推送
    AC自动机小结
    【GDB】用 python 扩展 gdb
    [Linux入门]---进程状态
    大一新生HTML期末作业 个人网页王嘉尔明星介绍网页设计与制作
    后缀系列
    深度学习之基于YoloV5交通信号标志识别系统
    SpringBoot整合WebService(服务端+客户端)
    外包干了20天,技术退步明显......
  • 原文地址:https://blog.csdn.net/lisainan66/article/details/132970492