zoukankan      html  css  js  c++  java
  • 4.java设计模式之原型模式

    基本需求

    • 有一个Sheep类的对象,我们现在需要创建100个和该对象属性完全一致的对象

    传统方式

    • 使用new关键字创建100个对象,将这一百个对象属性使用原型的get方法进行复制

    • 代码实现

      • // Sheep类
        @Data
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        public class Sheep {
        
           private String name;
        
           private int age;
        
        }
        
        // Client
        public static void main(String[] args) {
           Sheep sheep = new Sheep("zhangsan", 20);
           // 传统方式
           Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge());
           // ......
           System.out.println(sheep1);
        }
        
    • 缺陷及改进

      • 容易理解好操作,使用时需要频繁获取原型对象的属性,如果原型对象复杂,则效率很低,总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
      • 使用java基类中Object的clone()方法,可以复制对象,需要类实现Cloneable接口,该接口表示该类有复制能力,也就是原型模式

    基本介绍

    • 创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用

    • 实现方式:实现Cloneable接口,复写clone()方法或通过序列化,原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口

    • 优缺点:

      • 性能提高,逃避构造函数的约束
      • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候
      • 必须实现 Cloneable 接口
    • 使用场景:

      • 资源优化场景、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等
      • 性能和安全要求的场景、通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
      • 一个对象多个修改者的场景、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
      • 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者
    • UMl类图

    • 代码实现

      • @Data
        @ToString
        @NoArgsConstructor
        @AllArgsConstructor
        public class Sheep implements Serializable, Cloneable {
        
           private String name;
        
           private int age;
        
           // 默认的super.clone()实现的浅拷贝,即对于基本类型和字符串拷贝直接赋值,对于引用类型的直接拷贝的引用(并没有重新创建该内部的对象)
           private Friend friend;
        
           @Override
           protected Object clone() throws CloneNotSupportedException {
               // 如果没有实现Cloneable接口 调用该方法会抛出CloneNotSupportedException异常
               return super.clone();
           }
        
        }
        
        public class Client {
           public static void main(String[] args) throws CloneNotSupportedException {
               Friend friend = new Friend("666");
               Sheep sheep = new Sheep("zhangsan", 23, friend);
               // 使用默认clone方法 采用的是浅拷贝
               Sheep sheep1 = (Sheep) sheep.clone();
               Sheep sheep2 = (Sheep) sheep.clone();
               System.out.println(sheep1.toString());
               System.out.println(sheep2.toString());
               // 结果是true 说明clone出来的sheep1和sheep2中引用的是同一Friend对象,并没有将Friend对象再克隆一份,是浅拷贝
               // 深拷贝则是将Friend对象再克隆一份,sheep1和sheep2中引用的不是同一Friend对象 结果为false
               System.out.println(sheep1.getFriend() == sheep2.getFriend());
           }
        }
        

    spring源码

    • 在spring中AbstractBeanFactory类中doGetBean()方法中有使用到原型模式,配置bean标签时,有个属性为Scope

      • if (mbd.isSingleton()) {
           sharedInstance = this.getSingleton(beanName, () -> {
               try {
                   return this.createBean(beanName, mbd, args);
               } catch (BeansException var5) {
                   this.destroySingleton(beanName);
                   throw var5;
               }
           });
           bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
           // 此处判断了是否使用了prototype
        } else if (mbd.isPrototype()) {
           var11 = null;
        
           Object prototypeInstance;
           try {
               this.beforePrototypeCreation(beanName);
               prototypeInstance = this.createBean(beanName, mbd, args);
           } finally {
               this.afterPrototypeCreation(beanName);
           }
        }
        

    浅拷贝和深拷贝

    • 默认的super.clone()实现的浅拷贝,即对于基本类型和字符串拷贝直接赋值,对于引用类型的直接拷贝的引用(并没有重新创建该内部的对象)

    • 深拷贝则是将对象内部引用类型的属性会创建新的对象,使用的不是同一个对象

    • 深拷贝实现方式

      • 重写clone()方法,需要实现Cloneable接口
      • 使用序列化,需要实Serializable
    • 代码实现

    • // Sheep 类 重写clone方法 和 增加deepClone方法
      @Data
      @ToString
      @NoArgsConstructor
      @AllArgsConstructor
      public class Sheep implements Serializable, Cloneable {
      
         private String name;
      
         private int age;
      
         // 默认的super.clone()实现的浅拷贝,即对于基本类型和字符串拷贝直接赋值,对于引用类型的直接拷贝的引用(并没有重新创建该内部的对象)
         private Friend friend;
      
         @Override
         protected Object clone() throws CloneNotSupportedException {
             // 如果没有实现Cloneable接口 调用该方法会抛出CloneNotSupportedException异常
             // return super.clone();
      
             // 实现深拷贝方式一 重写clone方法 此种方法不好用
             // 如果最外层对象有多个引用类型的属性,则每个引用属性都要进行克隆,很麻烦
             // 如果层对象内部引用类型的属性仍然有引用类型的属性,则需要多层克隆,不易实现
             // 1.先浅拷贝最外层的对象
             Sheep sheep = (Sheep) super.clone();
             // 2.再拷贝外层对象内部引用类型的属性
             if (null != sheep.getFriend()) {
                 sheep.setFriend((Friend) sheep.getFriend().clone());
             }
             return sheep;
         }
      
         public Object deepClone() {
             // 实现深拷贝方式二 序列化方式
             ByteArrayOutputStream byteArrayOutputStream = null;
             ObjectOutputStream objectOutputStream = null;
             ByteArrayInputStream byteArrayInputStream = null;
             ObjectInputStream objectInputStream = null;
             try {
                 byteArrayOutputStream = new ByteArrayOutputStream();
                 objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                 // 将当前对象写入到字节数组流中
                 objectOutputStream.writeObject(this);
                 byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                 objectInputStream = new ObjectInputStream(byteArrayInputStream);
                 // 从字节数组流中读取对象并返回
                 return (Sheep) objectInputStream.readObject();
             } catch (Exception e) {
                 e.printStackTrace();
                 return null;
             } finally {
                 try {
                     objectInputStream.close();
                     byteArrayInputStream.close();
                     objectOutputStream.close();
                     byteArrayOutputStream.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
      
      }
      

    注意事项

    • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
    • 不用重新初始化对象,而是动态地获得对象运行时的状态
    • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
    • 在实现深克隆的时候可能需要比较复杂的代码
    • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
  • 相关阅读:
    基于Struts1框架的简单工程搭建
    Struts jar包下载地址
    Spring jar包下载地址
    java中ResourceBundle和Locale的简单使用
    Java Timer和TimerTask的使用
    Eclipse中Java build path的使用
    Java中出现No enclosing instance of type XXX is accessible问题
    Django 学习笔记(七)数据库基本操作(增查改删)
    Django 学习笔记(六)MySQL配置
    Django 学习笔记(五)模板标签
  • 原文地址:https://www.cnblogs.com/xiaokantianse/p/13962403.html
Copyright © 2011-2022 走看看