秒懂设计模式(五)——原型模式,你真的了解原型模式吗

秒懂设计模式(五)——原型模式,你真的了解原型模式吗,第1张

前言

        设计模式的本质在于抽象、解耦,用抽象来隔离变化。将复杂的事务按照六大设计原则,分解成一个个单一职责的个体。换而言之,是个体的高内聚和简单化,然后再组合到一起完成职能。合理使用设计模式,可以使程序设计更加标准化、代码编制更加工程化,使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

        上一篇文章中,我们已经聊完了GOF23中的第四个模式——建造者模式,如果没有看过,可以回顾一下。

创建型模式的工作原理

        创建型模式提供了一种创建对象的机制,抽象实例化的过程,隐藏了对象的创建细节,对外只提供一个通用接口,能够提升已有代码的灵活性和可复⽤性。创建型模式有五种:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。本文带着大家重新学习第五种设计模式——原型模式(Prototype Pattern)。

原型模式

定义

原型模式的定义如下:

        Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.

        用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式本质是一种克隆对象的方法,其核心是重写Object中的clone方法,调用该方法可以在内存中进行对象拷贝。

        Java提供了一个标记接口——Cloneable,实现该接口完成标记,在JVM中具有这个标记的对象才有可能被拷贝。如果不实现该接口,克隆对象会抛出CloneNotSupportedException异常。

 

         原型模式可以理解为:一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为生产需要的对象。

结构

 原型模式的结构很简单

1、Prototype抽象原型类,声明了clone方法,它可以是接口或基类,一般情况下可以不用抽象原型类。因为万物皆对象,在Java中Object类是所有类的父类,Object类中有clone方法。

 2、ConcretePrototype具体原型类,实现或者重写clone方法,通用代码如下:

@Data
public class ConcretePrototype implements Cloneable {

    private Long num;

    private List list;

    @Override
    public ConcretePrototype clone() {
        ConcretePrototype concretePrototype = null;
        try {
            concretePrototype = (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return concretePrototype;
    }
} 

模式分析

        我们使用new关键字去创建对象时,JVM虚拟机接收到指令,首先会去检查这个指令的参数是否能在常量池中定位到某个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化,如果没有,就必须先执行对应的类加载过程。紧接着JVM要为新对象分配内存空间、初始化零值、对对象头进行必要的设置,在JVM层面一个新的对象已经创建完成,但是从程序视角看,对象创建才刚刚开始,会接着执行方法完成对象初始化。如果大家对JVM虚拟机感兴趣,后续我会给大家详细介绍一下。

        原型模式在克隆对象时,不会执行对应类的构造方法,而是在内存中(具体地说是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块。

原型模式有两种实现方式——浅拷贝和深拷贝

浅拷贝:当拷贝对象包含基本数据类型(如int、long)或者不可变的对象(如字符串、基本类型的包装类)时,会直接将这些属性复制到新的对象中。而原型对象中的引用对象会把内存中的地址复制给克隆对象。此时,两个对象共享了一个私有变量,你改我改大家都能改。

深拷贝:不管原型对象属性是简单数据类型还是引用对象类型都会完全的复制一份到新的对象中。两个对象之间互不影响。

那原型模式该如何实现深拷贝呢?

@Data
public class ConcretePrototype implements Serializable, Cloneable {

    private Long num;

    private ArrayList list;

    @Override
    public ConcretePrototype clone() {
        ConcretePrototype concretePrototype = null;
        try {
            // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
            ByteArrayOutputStream bas = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bas);
            oos.writeObject(this);
            // 再将流序列化成对象
            ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            concretePrototype = (ConcretePrototype) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return concretePrototype;
    }
} 

原理:

        序列化需要使用的两个类:ObjectOutputStream类用于序列化、ObjectInputStream用于反序列化。

        首先使用ObjectOutputStream类将对象写入到ByteArrayOutputStream中,然后将ByteArrayOutputStream中的字节序列递交给ByteArrayInputStream,最后使用ObjectInputStream将ByteArrayInputStream中的字节序列重新反转为一个对象。

        注意:深拷贝比浅拷贝需要更多的资源,按照实际场景选择。现在已经有很多针对浅深拷贝的工具类

深拷贝:SerializationUtils

浅拷贝:BeanUtils

原型模式的优点

1、原型模式是在内存中进行二进制流的拷贝,要比直接new一个对象性能好,特别是在一个循环体内创建大量对象时。

2、原型模式可以简化对象创建的过程,可以直接拷贝现有的原型实例的值,实现对象复用。

适用场景

1、性能优化场景

类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

2、安全访问场景

当某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。

3、一个对象多个修改者的场景

一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

总结

        设计模式是一种思想,在不同的场景合理的运⽤可以提升整体的架构的质量。不要去生搬硬套,否则将会引起过渡设计,增加维护成本。到此五种创建型模式就都讲完了,后续会接着分享结构型模式,可以点下关注,和大家一起学习进步。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/719633.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)