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

    定义

    用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。可以简单类比于孙悟空用毫毛变化出很多和自己一模一样的小猴兵。

    结构

    • Prototype,原型接口,定义了克隆自身的方法。
    • ConcretePrototype,具体原型类,实现了原型接口。
    • Client,使用原型的客户端。

    简单实现

    原型接口

    public interface Prototype {
    
      /**
       * 克隆自身
       */
      Prototype clone();
    
    }
    

    具体原型类

    public class ConcretePrototype implements Prototype {
    
      private String name;
      private int age;
    
      public ConcretePrototype(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      public String getName() {
        return name;
      }
    
      public int getAge() {
        return age;
      }
    
      @Override
      public Prototype clone() {
        return new ConcretePrototype(this.name, this.age);
      }
    
      @Override
      public String toString() {
        return this.name + "," + age;
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype prototype = new ConcretePrototype("lisi", 24);
        System.out.println(prototype);//lisi,24
        //根据原型对象克隆出新的对象
        ConcretePrototype clonedPrototype = (ConcretePrototype) prototype.clone();
        System.out.println(clonedPrototype);//lisi,24
        System.out.println(prototype == clonedPrototype);//false
        System.out.println(prototype.getName() == clonedPrototype.getName());//true
      }
    
    }
    

    java本身提供了 Cloneable 接口,我们只需要重写 Object 中的 clone()方法就可以了

    public class ConcretePrototype implements Cloneable {
    
      private String name;
      private int age;
    
      public ConcretePrototype(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      public String getName() {
        return name;
      }
    
      public int getAge() {
        return age;
      }
    
      @Override
      public Object clone() {
        try {
          return super.clone();
        } catch (CloneNotSupportedException e) {
          throw new InternalError(e);
        }
      }
    
      @Override
      public String toString() {
        return this.name + "," + age;
      }
    }
    

    客户端代码和上面一样,Cloneable 接口下没有定义方法,只是作为一个声明,如果重写了clone()方法但没有实现此接口,就会抛出 CloneNotSupportedException 异常。

    原型模式在JDK的实现

    JDK中ArrayList

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
        /**
         * Returns a shallow copy of this {@code ArrayList} instance.  (The
         * elements themselves are not copied.)
         *
         * @return a clone of this {@code ArrayList} instance
         */
        public Object clone() {
            try {
                ArrayList<?> v = (ArrayList<?>) super.clone();
                v.elementData = Arrays.copyOf(elementData, size);
                v.modCount = 0;
                return v;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError(e);
            }
        }
    }
    

    客户端使用

    import java.util.ArrayList;
    
    public class TestArrayListClone2 {
    
      public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        names.add("lisi");
        names.add("lisi2");
        System.out.println(names);
        ArrayList<String> clonedNames = (ArrayList<String>) names.clone();
        System.out.println(clonedNames);
      }
    
    }
    

    其他类似的LinkedList,HashMap也都实现了 Cloneable 接口。

    浅克隆和深克隆

    • 浅克隆:只负责克隆基本数据类型的数据
    • 深克隆:基本数据类型和引用类型的数据都会被克隆,如果属性中还包含引用类型的属性,会一直递归的克隆下去。

    ArrayList等类中的克隆实现都是浅克隆,一般通过序列化方式来实现深克隆。

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    import java.util.ArrayList;
    
    public class TestArrayListClone {
    
      public static void main(String[] args) {
        testShallowCopy();
        testDeepCopy();
      }
    
      private static void testShallowCopy() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("lisi"));
        ArrayList<User> clonedUsers = (ArrayList<User>) users.clone();
        System.out.println("====浅克隆====");
        System.out.println(clonedUsers.size() == users.size());
        System.out.println(clonedUsers == users);
        System.out.println(clonedUsers.get(0) == users.get(0));
      }
    
      private static void testDeepCopy() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("lisi"));
        ArrayList<User> clonedUsers = (ArrayList<User>) deepCopy(users);
        System.out.println("====深克隆====");
        System.out.println(clonedUsers.size() == users.size());
        System.out.println(clonedUsers == users);
        System.out.println(clonedUsers.get(0) == users.get(0));
      }
    
      private static Object deepCopy(Object obj) {
        try {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos);
          oos.writeObject(obj);
          byte[] data = baos.toByteArray();
          ByteArrayInputStream bais = new ByteArrayInputStream(data);
          ObjectInputStream bis = new ObjectInputStream(bais);
          return bis.readObject();
        } catch (IOException | ClassNotFoundException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      private static class User implements Serializable {
    
        public User(String name) {
          this.name = name;
        }
    
        String name;
      }
    }
    

    输出结果为

    ====浅克隆====
    true
    false
    true
    ====深克隆====
    true
    false
    false
    

    可以看到,浅克隆的结果中User对象是同一个,深克隆结果中User对象是两个不同的对象。

    总结

    优点

    1. 当对象实例比较复杂时,使用原型模式可以简化创建新对象的过程,并且 java 自带的克隆实现是基于内存中二进制流的复制,相比于直接 new 一个对象,性能上更加优良。

    缺点

    1. clone方法位于类的内部,当对已有类进行改造的时候,可能也需要修改克隆方法,违背了开闭原则。
    2. 在不使用序列化的情况下,为了支持深克隆,当对象之间存在多重嵌套引用关系时,每一层对象都必须支持深克隆,实现起来可能比较麻烦。

    本质

    原型模式的本质是克隆生成对象,克隆是手段,生成对象是目的。

    使用场景

    1. 当创建新对象成本较大时(例如初始化需要较长的时间,占用太多的CPU资源或者网络资源),这种情况下可以考虑使用原型模式来创建新对象。

    参考

    大战设计模式【23】—— 原型模式
    设计模式的征途—5.原型(Prototype)模式
    研磨设计模式-书籍

  • 相关阅读:
    chroot 与 jail
    3-07. 求前缀表达式的值(25) (ZJU_PAT数学)
    一种监控全部账户登陆及操作命令的方法
    怎样设计接口?
    UVA10869
    网络直播电视之M3U8解析篇 (下)
    LCD显示--Ht1621b芯片显示屏驱动
    混淆
    android 调试
    eclipse+webservice开发实例
  • 原文地址:https://www.cnblogs.com/strongmore/p/15139174.html
Copyright © 2011-2022 走看看