• 创建型设计模式之原型模式


    原型模式

    概述

    原型模式(Prototype Pattern)是属于创建型模式

    它指用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

    原型模式的核心在于拷贝原型对象。以存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时的对象初始化过程(不调用构造函数),性能提升许多。

    当直接创建对象的代价比较大时,则采用这种模式。利用当前系统中已存在的对象作为原型,对其进行克隆,避免初始化的过程。

    优缺点

    优点:

    1.java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升许多
    
    2.使用原型模式将对象复制一份并将其状态保存起来,简化创建对象的过程,以便在需要的时候使用
    
    • 1
    • 2
    • 3

    缺点:

    1.需要为每一个类配置一个克隆方法
    
    2克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反开闭原测
    
    3.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支特深克隆,实现比较麻烦
    
    • 1
    • 2
    • 3
    • 4
    • 5

    应用场景

    1.类初始化消耗资源较多
    
    2.new产生的一个对象需要非常繁琐的过程(数据准备或访问权限)
    
    3.构造函数处比较复杂
    
    4.循环体中生产大量对象时
    
    5.资源优化场景
    
    6.性能和安全要求的场景
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    主要角色

    1.客户(Client)角色

    客户端类向原型管理器提出创建对象的请求。

    2.抽象原型(Prototype)角色

    这是一个抽象角色,此角色给出所有的具体原型类所需的接口。

    3.具体原型(Concrete Prototype)角色

    被复制的对象。此角色需要实现抽象的原型角色所要求的接口。

    4.原型管理器(Prototype Manager)角色

    创建具体原型类的对象,并记录每一个被创建的对象。

    原型模式的基本使用

    创建原型接口

    public interface IPrototype<T> {
        T clone();
    }
    
    • 1
    • 2
    • 3

    创建具体需要克隆对象

    @Data
    @ToString
    public class ConcretePrototype implements IPrototype {
    
        private int age;
        private String name;
    
        @Override
        public ConcretePrototype clone() {
            ConcretePrototype concretePrototype = new ConcretePrototype();
            concretePrototype.setAge(this.age);
            concretePrototype.setName(this.name);
            return concretePrototype;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用

        public static void main(String[] args) {
            //创建原型对象
            ConcretePrototype prototype = new ConcretePrototype();
            prototype.setAge(20);
            prototype.setName("jack");
            System.out.println(prototype);
    
            //拷贝原型对象
            ConcretePrototype cloneType = prototype.clone();
            System.out.println(cloneType);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    JDK自带原型接口的使用

    原型模式就是如此简单,通常开发中不会这样使用。其实JDK提供了一个现成的API接口,那就是Cloneable接口。

    @Data
    @ToString
    public class ConcretePrototype implements Cloneable {
    
        private int age;
        private String name;
        private List<String> hobbies;
    
        @Override
        public ConcretePrototype clone() {
            try {
                return (ConcretePrototype)super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    浅克隆与深度克隆

    浅克隆

    添加一个hobby属性,当给克隆对象的hobby属性添加一项时,最终结果会导致原型对象发生变化,也就是hobby属性用于一个内存地址。这就是浅克隆。

    @Data
    @ToString
    public class ConcretePrototype implements Cloneable {
    
        private int age;
        private String name;
        private List<String> hobby;
    
        @Override
        public ConcretePrototype clone() {
            try {
                return (ConcretePrototype)super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
        public static void main(String[] args) {
            ConcretePrototype prototype = new ConcretePrototype();
            prototype.setAge(22);
            prototype.setName("jack");
            List<String> hobby = new ArrayList<String>();
            hobby.add("java");
            hobby.add("python");
            hobby.add("go");
            prototype.setHobby(hobby);
    
            // 拷贝原型对象
            ConcretePrototype cloneType = prototype.clone();
            cloneType.getHobby().add("php");
    
    
            System.out.println("原型对象:" + prototype);
            System.out.println("克隆对象:" + cloneType);
            System.out.println(prototype == cloneType);
            System.out.println(prototype.getHobby() == cloneType.getHobby());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    原型对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
    克隆对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
    false
    true
    
    • 1
    • 2
    • 3
    • 4

    深度克隆

    可使用序列化方式实现对象的深度克隆

    @Data
    @ToString
    public class ConcretePrototype implements Cloneable, Serializable {
    
        private int age;
        private String name;
        private List<String> hobby;
    
        @Override
        public ConcretePrototype clone() {
            try {
                return (ConcretePrototype) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public ConcretePrototype deepClone() {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(this);
    
                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
    
                return (ConcretePrototype) ois.readObject();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    • 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
        public static void main(String[] args) {
            //创建原型对象
            ConcretePrototype prototype = new ConcretePrototype();
            prototype.setAge(22);
            prototype.setName("jack");
            List<String> hobby = new ArrayList<String>();
            hobby.add("java");
            hobby.add("python");
            hobby.add("go");
            prototype.setHobby(hobby);
    
            // 拷贝原型对象
            ConcretePrototype cloneType = prototype.deepClone();
            cloneType.getHobby().add("php");
    
            System.out.println("原型对象:" + prototype);
            System.out.println("克隆对象:" + cloneType);
            System.out.println(prototype == cloneType);
            System.out.println(prototype.getHobby() == cloneType.getHobby());
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    原型对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go])
    克隆对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
    false
    false
    
    • 1
    • 2
    • 3
    • 4

    也可这样操作,多克隆一次

        public ConcretePrototype deepClone2() {
            try {
                ConcretePrototype result = (ConcretePrototype) super.clone();
                result.hobby = (List) ((ArrayList) result.hobby).clone();
                return result;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    单例的破坏

    如果克隆的目标对象是单例对象,深克隆就会破坏单例。

    解决方案:禁止深克隆。

    1.让单例类不实现Cloneable接口
    
    2.重写clone方法,在clone方法中返回单例对象即可
    
    • 1
    • 2
    • 3
    @Data
    @ToString
    public class ConcretePrototype implements Cloneable {
    
        private static  ConcretePrototype instance = new ConcretePrototype();
    
        private ConcretePrototype(){}
    
        public static ConcretePrototype getInstance(){
            return instance;
        }
    
        @Override
        public ConcretePrototype clone() {
           return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    【示波器专题】示波器探头的负载效应
    Spring Cloud Gateway 网关的搭建 gateway实现自定义全局过滤器和自定义局部过滤器
    网络安全知识渗透测试
    docker 安装 portainer 来管理容器 (记录 1)
    C++学习day7
    Java8 新特性之Stream(八)-- Stream的collect()与Collectors的联合运用
    python实现图像二分类精准率(numpy)
    小扎宣布开放 Meta Horizo​​n OS
    纷享销客连接型CRM怎么样?
    程序员基础能力系列
  • 原文地址:https://blog.csdn.net/qq_38628046/article/details/126086291