原型模式

原型模式

标签 : Java与设计模式


原型模式
用原型实例指定创建对象的种类, 并通过拷贝这些原型创建新的对象.

原型模式就是通过一个对象再创建还有一个可定制的对象, 并且不须要知道不论什么创建的细节.
因此, 原型模式要求对象实现一个能够“克隆”自身的接口,这样就能够通过复制一个实例对象本身来创建一个新的实例。

这样一来。通过原型实例创建新的对象,就不再须要关心这个实例本身的类型,仅仅要实现了克隆自身的方法。就能够通过这种方法来获取新的对象,而无须再去通过new来创建。标准的原型模式的URL类图为:

但因为Java类库已经帮我们实现了一个java.lang.Cloneable接口, 因此我们的实现就简单了些,以下我们模仿多利羊的诞生过程:

/**
 * 该羊支持克隆
 * (浅复制)
 * Created by jifang on 15/12/9.
 */
public class Sheep implements Cloneable {

    private String name;
    private Date birthday;

    public Sheep() {
    }

    public Sheep(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

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

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

    @Override
    protected Sheep clone() throws CloneNotSupportedException {
        return (Sheep) super.clone();
    }
}
  • Client
public class Client {

    @Test
    public void client() throws CloneNotSupportedException {
        Date birthday = new Date();
        Sheep mother = new Sheep("多利母亲", birthday);
        System.out.println(mother);

        Sheep dolly = mother.clone();
        dolly.setName("多利");
        System.out.println(dolly);
    }
}

浅复制与深复制

  • 浅复制
    在上面的Client上加入几行代码, 就能够发现问题:
@Test
public void client() throws CloneNotSupportedException {
    Date birthday = new Date();
    Sheep mother = new Sheep("多利母亲", birthday);
    System.out.println(mother);

    Sheep dolly = mother.clone();
    dolly.setName("多利");
    System.out.println(dolly);

    birthday.setTime(123123123123L);
    System.out.println("dolly birthday: " + dolly.getBirthday());
}

执行上面的代码我们能够看到更改了mother的birthday, dolly的birthday也更改了. 这个问题是由浅复制引起的.

浅复制:
创建当前对象的浅复制版本号, 方法是创建一个新对象, 然后将当前对象的非静态字段拷贝到该新对象. 假设字段是值类型, 则对该字段执行逐位复制; 假设字段是引用类型, 则复制引用但不复制引用的对象; 因此原始对象及其副本引用同一对象;

此时执行的内存简化图例如以下:

能够看到mother和dolly的birthday指向同一个date对象.

  • 深复制版本号
    深复制: 把引用对象的变量指向复制过来的新对象, 而不是原有的被引用的对象;
@Override
protected Sheep clone() throws CloneNotSupportedException {
    Sheep sheep = (Sheep) super.clone();
    // 加入以下代码支持深复制
    sheep.birthday = (Date) this.birthday.clone();
    return sheep;
}

因此我们把Sheep的clone方法改成上面实现后, 再调用client方法就不会出现故障了, 此时的内存简图例如以下:

  • 利用序列化实现深复制
    Sheep须要先实现Serializable接口.
@Override
protected Sheep clone() throws CloneNotSupportedException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try {
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        return (Sheep) ois.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}

原型模式性能測试

假设创建Sheep是比較耗时的, 在此基础上进行原型模式的额性能測试:

/**
 * 模拟耗时的对象创建过程
 *
 * @param name
 * @param birthday
 */
public Sheep(String name, Date birthday) {
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    this.name = name;
    this.birthday = birthday;
}
  • 測试Client
public class Client {
    private static final int OBJECT_COUNT = 1000;

    private long testNew() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < OBJECT_COUNT; ++i) {
            Sheep sheep = new Sheep("name", new Date());
        }
        return System.currentTimeMillis() - start;
    }

    private long testPrototype() throws CloneNotSupportedException {
        long start = System.currentTimeMillis();
        Sheep sheep = new Sheep("name", new Date());
        for (int i = 0; i < OBJECT_COUNT; ++i) {
            Sheep newSheep = sheep.clone();
            newSheep.setName("new name");
        }
        return System.currentTimeMillis() - start;
    }

    @Test
    public void client() throws CloneNotSupportedException {
        System.out.println(testNew());
        System.out.println(testPrototype());
    }
}

执行上面程序, 能够非常明显看出new与原型模式的性能差异, 假设吧Sheep构造器中的sleep凝视掉, new与clone尽管有差异, 可是差距较小.

应用场景:

  • 原型模式非常少单独出现, 一般都是与工厂方法模式一起出现, 通过clone方法创建一个对象, 然后由工厂返回给调用者. 像Spring中的Bean的创建模式就有singleton与prototype模式.

參考:

posted @ 2017-07-22 16:46  zsychanpin  阅读(148)  评论(0编辑  收藏  举报