【设计模式】 原型模式

系列文章目录


文章目录


在本篇文章里,我们设定为制造简历,需求是要求有一个简历类,必须要有姓名,可以设置性别和年龄,可以设置工作经历,最终需求三分简历。

简历代码初步实现

java 复制代码
//简历类
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.sex = sex;
           this.age = age;
       }
       public void setWorkExperience(String timeArea,String company){
           this.company = company;
           this.timeArea = timeArea;
       }
       public void display(){
           System.out.println(this.name + " " + this.sex + " " + this.age);
           System.out.println("工作经历 " + this.timeArea + " " + this.company);
       }
    }
	//客户端
    public static void main(String[] args) {
        Resume resume1 = new Resume("小张");
        resume1.setPersonalInfo("男" , "XX公司");
        resume1.setWorkExperience("2024-2025", "XX公司");
        Resume resume2 = new Resume("小张");
        resume2.setPersonalInfo("男","XX公司");
        resume2.setWorkExperience("2024-2025" ,"XX公司");
        Resume resume3 = new Resume("小张");
        resume3.setPersonalInfo("男", "XX公司");
        resume3.setWorkExperience("2024-2025","XX公司");
        
        resume1.display();
        resume2.display();
        resume3.display();
    }

这里从我们所写的代码可以发现,三份简历需要三次实例化。这样的客户端会很麻烦,如果要20份简历的话,那我们就需要进行二十次实例化。如果我们有一处写错了,那么要修改也需要修改二十次。这里我们有另外一种写法。

java 复制代码
public static void main(String[] args) {
        Resume resume1 = new Resume("小张");
        resume1.setPersonalInfo("男" , "XX公司");
        resume1.setWorkExperience("2024-2025", "XX公司");
        
        Resume resume2 = resume1;
        Resume resume3 = resume1;
        resume1.display();
        resume2.display();
        resume3.display();
        
    }

这里其实是传引用,而不是传值,这样做就如同实在resume2的纸张和resume3的纸张上写着简历在resume1处一样,没有实际内容

我们知道有克隆Clone的方法。原型模式就是主要使用的这个方法。

原型模式

在介绍克隆前,我们要先提一个设计模式。

原型模式 : 用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象

原型模式其实就是从一个对象在创建到另外一个可定制的对象,而且不需要知道任何创建的细节。 ,我们来看基本的原型模式代码。

java 复制代码
 //原型类
    abstract class Prototype implements Cloneable{
        private String id;
        public Prototype(String id){
            this.id = id;
        }
        public String getID(){
            return this.id;
        }
        public Object clone(){
            Object object = null;
            try{
                object = super.clone();
            }catch(CloneNotSupportedException exception){
                System.out.println("Clone 异常");
            }
            return object;
        }
    }
    //具体原型类
    class ConcretePrototype extends Prototype{

        public ConcretePrototype(String id) {
            super(id);
        }
    }
    
	//客户端代码
    public static void main(String[] args) {
        ConcretePrototype p1 = new ConcretePrototype("编号123456");
        System.out.println("原ID " + p1.getID());
        ConcretePrototype c1 = (ConcretePrototype) p1.clone();
        System.out.println("克隆ID" + c1.getID());
    }

这样的话可以不用实例化ConcretePrototype,直接克隆就可以,但是对于Java而言,那个原型的抽象类Protype是用不着的,因为克隆实在是太常用了,所以Java提供了Cloneable接口, ,其中就是唯一的一个方法clone(); 这样你就只需要实现这个接口就可以完成原型模式了。

现在我们来修改原来的简历模型。

简历代码的原型实现

第二版代码:

java 复制代码
//简历
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.sex = sex;
            this.age = age;
        }
        public void setWorkExperience(String timeArea,String company){
            this.company = company;
            this.timeArea = timeArea;
        }
        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 exception){
                System.out.println("Clone异常");
            }
            return object;
        }
    }
    //客户端
    public static void main(String[] args) {
        Resume resume1 = new Resume("小张");
        resume1.setPersonalInfo("男" , "XX公司");
        resume1.setWorkExperience("2024-2025", "XX公司");
        //调用clone()方法就可以实现新简历的生成
        Resume resume2 = resume1.clone();
        //可以修改新简历的细节
        resume2.setWorkExperience("2023-2025","YY集团");
        Resume resume3 =resume1.clone();
        resume3.setPersonalInfo("男" ,"24");
        resume1.display();
        resume2.display();
        resume3.display();
    }
    

这样的话,客户端代码就清晰多了,而且要想修改某份简历,我们只需要对这份简历做一定的修改就可以了,不会影响其他简历。

之前的话,每new一次都需要执行一次构造函数,如果构造函数执行时间很长,多次执行就太低效了,一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。不用重新初始化对象,
而是动态的获得对象运行时的状态。

浅复制与深复制

那么如果我们需要修改需求呢,因为简历对象里的数据都是String型的,而String是一种拥有值类型特点的特殊引用类型,super.clone()方法是这样,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用不复制引用的对象,因此,原始对象及副本引用同一个对象。 就是说如果你的"简历"类当中有对象引用,那么引用的对象数据是不会被克隆过来的。

下面我们用代码举例,现在简历类中有一个'设置工作经历'的方法,在设计时一般会有一个;工作经历类,当中有'时间区间'和'公司名称'等属性,'简历'类直接调用这个对象即可,代码如下:

java 复制代码
  //简历类
  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.sex = sex;
            this.age = age;
        }
        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 exception){
                System.out.println("Clone异常");
            }
            return object;
        }
    }
    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 static void main(String[] args) {
        Resume resume1 = new Resume("小张");
        resume1.setPersonalInfo("男" , "24");
        resume1.setWorkExperience("2024-2025", "XX公司");
        //调用clone()方法就可以实现新简历的生成
        Resume resume2 = resume1.clone();
        //可以修改新简历的细节
        resume2.setWorkExperience("2023-2025","YY集团");
        Resume resume3 =resume1.clone();
        resume3.setPersonalInfo("男" ,"26");
        resume3.setWorkExperience("2025","ZZ公司");
        resume1.display();
        resume2.display();
        resume3.display();
    }

这里我们期望的展示结果为

小张 男 24

工作经历 2024-2025 XX公司

小张 男 24

工作经历 2023-2025 YY公司

小张 男 26

工作经历 2025 ZZ公司

但是我们的事实结果是:

小张 男 24

工作经历 2025 ZZ公司

小张 男 24

工作经历 2025 ZZ公司

小张 男 26

工作经历 2025 ZZ公司

这里主要是因为它是浅表复制,所以对于值类型,没什么问题,但是对引用类型,就只是复制了一个引用,对引用的对象还是指向了原来的对象,所以就会出现我给resume1、resume2、resume3三个引用设置'工作经历',但却看到三个引用最后都是最后一次设置,因为三个引用都指向了一个对象。

以上这种情况就叫做浅复制,被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。 但是我们可能更需要这样的一种需求,把要复制的对象所引用的对象都复制一遍。 比如刚才那个例子,我们就希望是resume1,resume2,resume3三个应用的对象是不同的,复制时就一变二,二变三,此时,我们就称这种方式为深复制,深复制会把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象

如果出现了简历对象引用了工作经历,工作经历再引用公司,公司再引用职位,这样一个引用一个有很多层的话,该怎么办呢?这里深复制要深入到多少层,需要事先考虑好,而且当心出现循环引用的问题,需要小心处理,并且情况比较复杂,这里我们先对以上简历代码进行修改。

java 复制代码
 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 WorkExperience clone(){
            WorkExperience object = null;
            try{
                object = (WorkExperience) super.clone();
            }catch(CloneNotSupportedException exception){
                System.out.println("Clone异常");
            }
            return object;
        }
    }
   
    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.sex = sex;
            this.age = age;
        }
        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();
                //把克隆对象里的引用也进行克隆,即达到了深复制的目的
                object.work = this.work.clone();
            }catch(CloneNotSupportedException exception){
                System.out.println("Clone异常");
            }
            return object;
        }
    }

这样的代码就实现了深复制。

总结

以上就是本文全部内容,本文主要向大家介绍了设计模式中的原型模式,通过对简历模版的代码修改,引出了原型模式的代码模板。并介绍了浅复制与深复制的相关概念。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持!

相关推荐
带刺的坐椅12 分钟前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看2 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程2 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t2 小时前
ZIP工具类
java·zip
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan3 小时前
第10章 Maven
java·maven
百锦再4 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说4 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多4 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再4 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven