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

- 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对象是两个不同的对象。
总结
优点
- 当对象实例比较复杂时,使用原型模式可以简化创建新对象的过程,并且 java 自带的克隆实现是基于内存中二进制流的复制,相比于直接 new 一个对象,性能上更加优良。
缺点
- clone方法位于类的内部,当对已有类进行改造的时候,可能也需要修改克隆方法,违背了开闭原则。
- 在不使用序列化的情况下,为了支持深克隆,当对象之间存在多重嵌套引用关系时,每一层对象都必须支持深克隆,实现起来可能比较麻烦。
本质
原型模式的本质是克隆生成对象,克隆是手段,生成对象是目的。
使用场景
- 当创建新对象成本较大时(例如初始化需要较长的时间,占用太多的CPU资源或者网络资源),这种情况下可以考虑使用原型模式来创建新对象。
 
         
         
         
         
         
        
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号