创建型之原型模式

序言

  原型模式根据原型对象可以创建出完全一模一样的相同对象。
原型模式基本分为2类:

  1. 简单式原型模式:适用原型对象数目较少且比较固定;创建后直接被客户端使用
  2. 登记式原型模式:适用创建的原型对象数目不固定;复制对象前,先从容器中查找是否已经克隆,没有的话直接复制,并放入,有的话直接从容器中取出。

复制形式可分为2类:

  1. 浅复制:按值复制,对于引用类型,只是把地址复制了一份,复制后,新引用与旧引用指向的是同一个对象
  2. 深复制:基本数据类型按值复制,对于引用类型,其指向的对象会在内存中重新复制一份,复制后,新引用与旧引用指向的是不同的对象,但内容相同。深复制存在2个问题,1是可能会出现循环引用,2是复制的深度,具体到那一层是不确定的。

原型模式

原型模式的结构如下图:

![](http://images2017.cnblogs.com/blog/946528/201708/946528-20170809175623245-345309687.png)
> 套路: > 1. 实现Cloneable接口;深复制还要实现Serializable接口 > 2. 浅复制要实现clone方法
/**
 * 需要克隆的原型类
 */
public class Person implements Cloneable,Serializable{
    private String name;
    private Integer age;
    private Date birthday;

    public Person(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    /**
     * 浅复制
     * 1.clone()源自Object,访问修饰符为protected,这里改为public
     * 2.Person要实现标识接口Cloneable
     */
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 深复制
     * 1.Person实现Serializable接口
     */
    public Object deepClone() throws IOException, ClassNotFoundException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        return new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())).readObject();
    }
}
/**
 * 原型管理器
 */
public class PersonPrototypeManager {

    private static final Map<String,Person> personMap = new HashMap<String, Person>();

    private PersonPrototypeManager(){}

    public static synchronized void put(String personId,Person person){
        personMap.put(personId,person);
    }
    public static synchronized void remove(String personId){
        personMap.remove(personId);
    }
    public static synchronized Person get(String personId){
        return personMap.get(personId);
    }
}

/**
 * 测试原型模式
 */
public class PrototypeTest {

    @Test
    public void testClone() throws CloneNotSupportedException {
        //浅复制
        Person oldPerson = new Person("田小娥", 24, new Date());
        Person newPerson = (Person) oldPerson.clone();
        System.out.println(newPerson);
        System.out.println(oldPerson == newPerson);

        //注册到原型管理器中
        PersonPrototypeManager.put(newPerson.getName(),newPerson);
        System.out.println(PersonPrototypeManager.get(newPerson.getName()));

    }

    @Test
    public void testDeepClone() throws IOException, ClassNotFoundException {
        //深复制
        Person oldPerson = new Person("田小娥", 24, new Date());
        Person newPerson = (Person) oldPerson.deepClone();
        System.out.println(newPerson);
        System.out.println(oldPerson == newPerson);

        //注册到原型管理器中
        PersonPrototypeManager.put(newPerson.getName(),newPerson);
        System.out.println(PersonPrototypeManager.get(newPerson.getName()));

    }

}

吹牛:原型模式可以在不改变接口的情况下,动态的改变实现类对象。缺点是对于不支持序列化的类如Thread对象和Socket对象无能为力;深复制时可能会出现循环引用,导致程序奔溃。

后记

  感谢java-my-file,撰写本文参考了其文章《JAVA与模式》之原型模式
  转载时,请注明出处,这是人格的一种体现。
  https://www.zybuluo.com/BertLee/note/843705
  能力有限,如有纰漏,请在评论区指出,老朽虽一把年纪,必当感激涕零,泪如雨下。

posted @ 2017-08-09 17:57  吃不了兜着走  阅读(96)  评论(0编辑  收藏  举报