您的位置:首页 > 编程语言 > Java开发

原型模式

2015-12-20 17:38 337 查看

原型模式

标签 : 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模式.

参考:

《JAVA与模式》之原型模式

大话设计模式

高淇讲设计模式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息