• Java设计模式——原型模式讲解以及在Spring框架中的使用


    设计模式全篇即将上线在:https://javaxiaobear.gitee.io/

    1、克隆羊问题

    现在有一只熊tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和tom 熊属性完全相同的10只熊。

    1、代码实现
    public class Bear {
    
        private String name;
    
        private Integer age;
    
        private String color;
    
        public Bear(String name, Integer age, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        @Override
        public String toString() {
            return new StringJoiner(", ", Bear.class.getSimpleName() + "[", "]")
                    .add("name='" + name + "'")
                    .add("age=" + age)
                    .add("color='" + color + "'")
                    .toString();
        }
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    public class BearCloneTest {
    
        public static void main(String[] args) {
            Bear bear = new Bear("bear", 3, "白色");
            Bear bear2 = new Bear(bear.getName(), bear.getAge(), bear.getColor());
            Bear bear3 = new Bear(bear.getName(), bear.getAge(), bear.getColor());
            Bear bear4 = new Bear(bear.getName(), bear.getAge(), bear.getColor());
            Bear bear5 = new Bear(bear.getName(), bear.getAge(), bear.getColor());
    
            System.out.println(bear2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2、传统方式优缺点

    优点是比较好理解,简单易操作。

    在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

    总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

    3、改进的思路分析

    Java 中Object 类是所有类的根类,Object 类提供了一个clone()方法,该方法可以将一个Java 对象复制一份,但是需要实现clone 的Java 类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力=>原型模式

    2、原型模式

    1、介绍

    原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

    原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节

    工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()

    2、代码实现
    public class Bear implements Cloneable{
    
        private String name;
    
        private Integer age;
    
        private String color;
    
        public Bear(String name, Integer age, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        @Override
        protected Bear clone(){
            Bear bear = null;
            try {
                bear = (Bear) super.clone();
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return bear;
        }
    
        @Override
        public String toString() {
            return new StringJoiner(", ", Bear.class.getSimpleName() + "[", "]")
                    .add("name='" + name + "'")
                    .add("age=" + age)
                    .add("color='" + color + "'")
                    .toString();
        }
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    public class ImproveTest {
    
        public static void main(String[] args) {
            Bear bear = new Bear("bear", 3, "白色");
            Bear clone1 = bear.clone();
            Bear clone2 = bear.clone();
            Bear clone3 = bear.clone();
            Bear clone4 = bear.clone();
    
            System.out.println(clone1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、原型模式在Spring框架中的使用

    Spring中原型Bean的创建,就是原型模式的应用

    prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了

    <bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
    
    • 1

    4、浅拷贝与深拷贝

    1、浅拷贝
    • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

    • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

    • 克隆熊就是浅拷贝

    • 浅拷贝是使用默认的clone()方法来实现

      sheep = (Sheep) super.clone();
      
      • 1
    2、深拷贝

    复制对象的所有基本数据类型的成员变量值

    为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。

    深拷贝实现方式:

    • 重写clone 方法来实现深拷贝
    • 通过对象序列化实现深拷贝(推荐)
    1. 新建对象

      public class DeepCloneableTarget implements Cloneable, Serializable {
      
          public static final long serialVersionUID = 1L;
      
          private String cloneName;
      
          private String cloneClass;
      
          public DeepCloneableTarget(String cloneName, String cloneClass) {
              this.cloneName = cloneName;
              this.cloneClass = cloneClass;
          }
      
          /**
           * 因为该类的属性,都是String , 因此我们这里使用默认的clone 完成即可
           * @return
           * @throws CloneNotSupportedException
           */
          @Override
          protected Object clone() throws CloneNotSupportedException {
              return super.clone();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    2. 完成对象的引用

      public class DeepProtoType implements Cloneable, Serializable {
      
          public String name;
      
          public DeepCloneableTarget deepCloneableTarget;
      
          public DeepProtoType() {
              super();
          }
      
          /**
           * 利用clone进行拷贝
           * @return
           * @throws CloneNotSupportedException
           */
          @Override
          protected Object clone() throws CloneNotSupportedException {
              Object deep = null;
              //这里完成对基本数据类型(属性)和String 的克隆
              deep = super.clone();
              //进行转换,单独处理
              DeepProtoType deepProtoType = (DeepProtoType) deep;
              deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
              return deepProtoType;
          }
      
          public Object deepClone(){
              //创建流对象
              ByteArrayOutputStream bos = null;
              ObjectOutputStream oos = null;
              ByteArrayInputStream bis = null;
              ObjectInputStream ois = null;
              try {
                  //序列化
                  bos = new ByteArrayOutputStream();
                  oos = new ObjectOutputStream(bos);
                  //当前这个对象以对象流的方式输出
                  oos.writeObject(this);
      
                  //反序列化
                  bis = new ByteArrayInputStream(bos.toByteArray());
                  ois = new ObjectInputStream(bis);
                  DeepProtoType copyObj = (DeepProtoType)ois.readObject();
                  return copyObj;
              }catch (Exception e){
                  e.printStackTrace();
                  return null;
              }finally {
                  //关闭流
                  try {
                      bos.close();
                      oos.close();
                      bis.close();
                      ois.close();
                  } catch (Exception e2) {
                      // TODO: handle exception
                      System.out.println(e2.getMessage());
                  }
              }
          }
      }
      
      • 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
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
    3. 测试

      public class DeepTest {
      
          public static void main(String[] args) throws CloneNotSupportedException {
              DeepProtoType protoType = new DeepProtoType();
              protoType.name = "小熊";
              protoType.deepCloneableTarget = new DeepCloneableTarget("大熊", "小熊");
      
              //方式1 完成深拷贝
              DeepProtoType p2 = (DeepProtoType) protoType.clone();
      
              System.out.println("p.name=" + protoType.name + " p.deepCloneableTarget=" + protoType.deepCloneableTarget.hashCode());
              System.out.println("p2.name=" + protoType.name + " p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
      
              //方式二
              DeepProtoType p3 = (DeepProtoType) protoType.deepClone();
              System.out.println("p.name=" + protoType.name + " p.deepCloneableTarget=" + protoType.deepCloneableTarget.hashCode());
              System.out.println("p2.name=" + protoType.name + " p3.deepCloneableTarget=" + p3.deepCloneableTarget.hashCode());
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

    5、注意细节和说明

    创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

    不用重新初始化对象,而是动态地获得对象运行时的状态

    如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码

    在实现深克隆的时候可能需要比较复杂的代码

    缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp 原则,这点请同学们注意.

  • 相关阅读:
    JavaFx-初识
    跨境电商如何通过打好数据底座,实现低成本稳步增长
    4 Paimon数据湖之Hive Catalog的使用
    git的基础操作
    2022年半导体硅片行业研究报告
    ChatGPT Plus暂停注册,用户激增压力太大!
    java计算机毕业设计Web企业差旅在线管理系统源码+mysql数据库+系统+lw文档+部署
    SpringBoot整合Zookeeper做分布式锁
    网络安全-Web端安全协议
    实际业务中使用策略模式对代码进行重构
  • 原文地址:https://blog.csdn.net/Y_hanxiong/article/details/128048562