大聪明教你学Java设计模式 | 第四篇:原型模式

3,779 阅读5分钟

前言

这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战

大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案。大聪明本着“独乐乐不如众乐乐”的宗旨与大家分享一下设计模式的学习心得。

面向对象编程,顾名思义就是时时刻刻与对象打交道,有时候获取一个实例化的对象非常麻烦。比如一个需要访问数据库关联大量数据表才能得到一个实例,比如对象的属性非常非常多,通过构造函数获取对象需要初始化很多对象,比较麻烦,浪费内存。那么怎么解决类似的问题呢?这时候就需要请出原型模式来帮助我们解决问题啦。

举个例子,我们都玩过打飞机(咦??打飞机??)的游戏,也就是飞机大战,我们驾驶着战机跟敌人打斗,敌军战机也是数不胜数,但是如果每出一架敌机都要进行实例化的话,很显然我们的功能很复杂而且有着巨大代码量。所以这个时候原型模式就派上用场了,只需要实例化一架飞机出来,其他的照着他复制就可以了。

深克隆与浅克隆

在原型模式中有两个概念需要讲下,就是深克隆与浅克隆。 浅克隆(shallow clone): 指拷贝对象时仅仅克隆对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象(说的具体一点就是浅克隆只克隆对象中的基本数据类型,包括byte、short、int、long、float、double、boolean、char、String【八种基本数据类型和一个引用类型】,而对象中的其他非String引用对象类型则不会被克隆)。

深克隆(shallow clone): 什么都是单独的,全部复制,复制之后的全部各自独立。修改克隆对象对于原型对象没有任何影响(浅克隆中修改克隆对象,原型对象跟着变)。

下面通过代码来讲解一下这二者之间的区别。

浅克隆

首先我们新建一个学生类:

import java.io.Serializable;
import java.util.Date;

/**
 * @description: Student
 * @author: 庄霸.liziye
 * @create: 2021-08-09 15:26
 **/
public class Student implements Cloneable, Serializable {
    private String name;
    private Date birthday;

    public Student(){}
    public Student(String name, Date birthday) {
        super();
        this.name = name;
        this.birthday = birthday;
    }

    //实现浅克隆
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

接下来我们写一个测试类,来实现浅克隆

import java.util.Date;

/**
 * @description: test
 * @author: 庄霸.liziye
 * @create: 2021-08-09 15:30
 **/
public class test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date=new Date(12345L);
        //new一个学生:大聪明
        Student daCongMing=new Student("大聪明",date);
        System.out.println(daCongMing);
        System.out.println(daCongMing.getName());
        System.out.println(daCongMing.getBirthday());

        date.setTime(67890L);
        System.out.println(daCongMing.getBirthday());

        //克隆大聪明,给他克隆一个大漂亮当女朋友
        Student daPiaoLiang=(Student) daCongMing.clone();
        System.out.println(daPiaoLiang);
        daPiaoLiang.setName("大漂亮");
        System.out.println(daPiaoLiang.getName());
        System.out.println(daPiaoLiang.getBirthday());
    }
}

在这里插入图片描述 我们执行测试类会发现当birthday改变的时候,克隆对象的birthday也随之改变。这个不难理解,因为原型对象改变,克隆对象也随之改变,克隆对象只是克隆了Student对象,并没有去克隆Student对象中的birthday对象。 这也就证明了==浅克隆是克隆对象直接引用了原型对象。==

深克隆

下面我们对学生类进行加工,使其实现深克隆

import java.io.Serializable;
import java.util.Date;

/**
 * @description: Student
 * @author: 庄霸.liziye
 * @create: 2021-08-09 15:26
 **/
public class Student implements Cloneable, Serializable {
    private String name;
    private Date birthday;

    public Student(){}
    public Student(String name, Date birthday) {
        super();
        this.name = name;
        this.birthday = birthday;
    }

    //实现深克隆
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj= super.clone();
        Student s = (Student)obj;
        s.birthday = (Date) this.birthday.clone();
        return obj;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

接下来我们写一个测试类,来实现深克隆

public static void main(String[] args) throws CloneNotSupportedException {
        Date date=new Date(12345L);

        Student daCongMing = new Student("大聪明",date);
        Student daPiaoLiang = (Student) daCongMing.clone();//深克隆里面的birthday对象是一个新对象

        System.out.println(daCongMing);
        System.out.println(daCongMing.getName());
        System.out.println(daCongMing.getBirthday());

        date.setTime(67890L);
        System.out.println(daCongMing.getBirthday());

        System.out.println(daPiaoLiang);
        daPiaoLiang.setName("大漂亮");
        System.out.println(daPiaoLiang.getName());
        System.out.println(daPiaoLiang.getBirthday());
    }

此时我们再看一下执行结果 在这里插入图片描述 我们发现,当date改变的时候,克隆对象的birthday并没有随之改变。通过实体类可知,当克隆对象的时候,随之也去克隆了实体类里面的引用对象,即克隆对象的birthday是一个new的对象。 这也就证明了==深克隆是递归克隆出原型对象里的所有引用对象==。

小结

原型模式优点是显而易见的,当创建新的对象实例比较复杂时,使用原型模式通过一个已有实例可以提高新实例创建效率;可以动态增加或者减少产品类;原型模式提供了简单的创建结构;可以使用深克隆的方式保存对象的状态。同时它也有自己的弊端,对已有类进行改造时,必须修改其源码,违背了开闭原则;在实现深克隆时需要编写较为复杂的代码。 所以说原型模式就像是一把双刃剑,用好了,会极大的提高开发效率;用不好,就会对着一堆复杂代码懵逼。

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●'◡'●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

爱你所爱 行你所行 听从你心 无问东西