• 23种设计模式-原型设计模式介绍加实战代码


    软件设计模式白话文系列(三)原型模式

    1、描述

    用原型实例指定创建对象的种类,并且通过拷贝这些原型对象的属性来创建新的对象。通俗点的意思就是一个对象无需知道任何创建细节就可以创建出另外一个可定制的对象。可以简单看作为复制、粘贴操作。

    原型模式的克隆分为浅克隆和深克隆。

    • 浅克隆

    克隆对象的属性和原对象完全相同,基本类型的属性属于值传递,改变一个对象的值,另一个不会受影响。对于引用数据类型的属性,仍指向原有属性所指向的对象的内存地址。引用类型是传递引用,指向同一片内存空间,改变一个对象的引用类型的值,另一个对象也会随之改变。但是这里需要注意 String 类型却是一个特殊,String虽然属于引用类型,但是 String 类是不可改变的,它是一个常量,一个对象调用 clone 方法,克隆出一个新对象,这时候两个对象的同一个 String 类型的属性是指向同一片内存空间的,但是如果改变了其中一个,会产生一片新的内存空间,此时该对象的这个属性的引用将指向这片新的内存空间,此时两个对象的String类型的属性指向的就是不同的2片内存空间,改变一个不会影响到另一个,可以当做基本类型来使用。

    • 深克隆

    克隆对象的所有属性都会被克隆,不再指向原有对象地址。

    2、适用性

    • 对象之间相同或相似,即只是个别的⼏个属性不同的时候。
    • 创建对象成本较⼤,例如初始化时间⻓,占⽤CPU太多,或者占⽤⽹络资源太多等,需要优化资 源。
    • 创建⼀个对象需要繁琐的数据准备或访问权限等,需要提⾼性能或者提⾼安全性。
    • 系统中⼤量使⽤该类对象,且各个调⽤者都需要给它的属性重新赋值。

    该设计模式使用场景很广泛,日常开发中难免有操作某个对象时又不想影响原有对象的情况,具体还需要看业务需求使用浅克隆或者是深克隆。

    3、实现逻辑

    • 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
    • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
    • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

    Java 中通过 Object 类中提供的 clone() 方法和 Cloneable 接口来实现浅克隆,clone() 方法由 native 关键字修饰,通过本地方法拷贝地址值(基于内存二进制流的复制)来实现,不但效率高还免去我们手动实现的烦恼。 Cloneable 接口充当抽象原型类,而我们实现了 Cloneable 接口的子实现类就是具体的原型类。

    需要注意由于 Object 本身没有实现 Cloneable 接口,所以不重写 clone 方法并且进行调用的话会发生 CloneNotSupportedException 异常。

    4、实战代码

    4.1 浅克隆

    /**
     * 原型类引用类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-11-05 15:29:57
     */
    @Setter
    @Getter
    @ToString
    public class Account {
        private String name;
    }
    
    /**
     * 具体的原型类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-10-28 17:00:11
     */
    @Getter
    @Setter
    @ToString
    public class Member implements Cloneable {
    
        private int id;
    
        private String name;
    
        private Account account;
    
        @Override
        protected Member clone() throws CloneNotSupportedException {
            return (Member) super.clone();
        }
    }
    
    /**
     * 测试类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-11-05 15:34:36
     */
    public class ShallowClone {
        public static void main(String[] args) throws CloneNotSupportedException {
            Account account = new Account();
            account.setName("account");
    
            Member member = new Member();
            member.setId(1);
            member.setName("member");
            member.setAccount(account);
    
            Member cloneMember = member.clone();
    
            // clone 对象与原对象的比较
            System.out.println(cloneMember == member);
            System.out.println(cloneMember.getId() == member.getId());
            System.out.println(cloneMember.getAccount() == member.getAccount());
            System.out.println(cloneMember.getName() == member.getName());
    
            // 修改 clone 对象属性
            account.setName("newAccount");
            cloneMember.setId(2);
            cloneMember.setName("newMember");
    
            // 查看原型对象
            System.out.println(member);
            System.out.println(cloneMember);
        }
    }
    

    执行结果:

    从结果不难看出,我们得到的 clone 对象是一个新的对象,但是属性的值或者引用地址都是一样的。再修改 clone 对象后,基本类型的属性的值不会跟着修改, String 类型的属性由于自身特性指向了新的地址值,而我们 Account 类的属性,在 clone 对象的值被修改,两个对象的 account 属性都指向同一个内存地址值,所以会被一起修改。我在生产开发中需要注意原型模式对象引用属性谨慎操作。

    4.2 对象流深克隆

    进行深克隆需要使用对象流。不用实现 Cloneable 接口,注意实现序列化接口 Serializable

    /**
     * 原型类引用类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-11-05 15:29:57
     */
    @Setter
    @Getter
    @ToString
    public class Account implements Serializable {
        private String name;
    }
    
    /**
     * 具体的原型类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-10-28 17:00:11
     */
    @Getter
    @Setter
    @ToString
    public class Member implements Serializable {
    
        private int id;
    
        private String name;
    
        private Account account;
    }
    
    /**
     * 深克隆测试类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-11-05 16:26:57
     */
    public class DeepClone {
        public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
            Account account = new Account();
            account.setName("account");
    
            Member member = new Member();
            member.setId(1);
            member.setName("member");
            member.setAccount(account);
    
            // 创建对象输出流对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("XXX/member.txt"));
            // 将 member 对象写出到文件中
            oos.writeObject(member);
            oos.close();
    
            // 创建对象出入流对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("XXX/member.txt"));
            // 读取对象
            Member cloneMember = (Member) ois.readObject();
            // 修改引用对象
            Account newAccount = cloneMember.getAccount();
            newAccount.setName("newAccount");
    
            // 查看原型对象
            System.out.println(member);
            System.out.println(cloneMember);
        }
    }
    

    执行结果:

    通过序列化和反序列化得到的对象地址值不同来达到目的

    4.3 JSON 深克隆

    基于 Json 实现对象深度 Clone,不再需要实现 Clonable 接⼝,但是需要引用 JSON 工具包,这里使用 hutool-json

    
      cn.hutool
      hutool-json
      5.8.9
    
    

    测试代码:

    /**
     * JSON 方式深克隆测试类
     *
     * @author Eajur.Wen
     * @version 1.0
     * @date 2022-11-07 19:21:14
     */
    public class JsonDeepClone {
        public static void main(String[] args) {
            Account account = new Account();
            account.setName("account");
    
            Member member = new Member();
            member.setId(1);
            member.setName("member");
            member.setAccount(account);
    
            //将 member 对象写到 JSON 字符串
            String json = JSONUtil.toJsonStr(member);
    
            //json 字符串转换为 member对象
            Member cloneMember = (Member) JSONUtil.toBean(json, Member.class);
            //获取c2奖状所属学生对象
            Account newAccount = cloneMember.getAccount();
            newAccount.setName("newAccount");
    
            // 查看原型对象
            System.out.println(member);
            System.out.println(cloneMember);
        }
    }
    

    执行结果:

    可以使⽤深克隆⽅式保存对象的状态,使⽤原型模式将对象复制⼀份,并将其状态保存起来,简化了 创建对象的过程,以便在需要的时候使⽤(例如恢复到历史某⼀状态),可辅助实现撤销操作。

  • 相关阅读:
    深度学习手写字符识别:推理过程
    hping3网络延迟检测工具——筑梦之路
    如何用arduino去检测声音大小?
    【C语言】数组的强化训练(详细讲解+源码展示)
    NET:Spire.XLS 12.11.3 supports a variety of new formulas
    北京印刷学院计算机考研资料汇总
    绩效只是绩效
    QT widget
    外贸网站做Google SEO 用wordpress模板的优势
    是真的吗?Nuture子刊告诉你这么多年的微生物组经验都是错的?!
  • 原文地址:https://www.cnblogs.com/eajur/p/16860554.html