1.什么是原型模式?
原型模式是创建对象实例的一种方式,当一个对象需要被重复多次实例化时,可以考虑使用原型模式.原型模式通过创建出一个类的原型,然后调用这个原型对象的克隆方法,从而创建出跟该原型一毛一样的对象.
在传统情况下,我们是通过new来创建对象实例,但如果该对象需要被多次实例化,且实例化后要对其设置一系列初始值时,这时候用传统的做法会降低效率,同时也违背了DRY(don’t repeat youself)原则,如果采用原型模式,就可以让你的代码变得非常优雅又具有效率.
2.为什么要使用原型模式?
①可以让你的系统性能提高.
②可以逃避构造函数的约束.
3.如何实现原型模式?
因为原型模式的实现其实依赖于拷贝,在开始写代码前,我先解释下浅拷贝和深拷贝
浅拷贝:浅拷贝只复制一个对象,传递引用,不能复制实例,对于浅拷贝当对象的成员变量是基本数据类型时,两个对象的成员变量已有存储空间,赋值运算传递值,所以浅拷贝能够复制实例.但是当对象的成员变量是引用数据类型时,就不能实现对象的复制了.
深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值,这个方式称为深拷贝.
如果没太看懂,没关系,下面的例子我会进一步分析:
由于本人是个车迷,梦想能开上保时捷,于是我通过代码创建了一个保时捷,然后将其实例化了,后来我对这一辆保时捷不满足,想批量生产然后卖更多钱,于是我以这辆保时捷为原型,拷贝出N辆一毛一样的保时捷…
/**
* 保时捷
*/
@Data//这里我用Lombok插件了,如果没有请手动加上get set方法及toString
public class Porsche implements Cloneable {
private String name;
private Integer price;
private String color;
private Key key;
/**
* 浅拷贝克隆
* @return
*/
public Object clone() {
Porsche porsche = null;
try {
porsche = (Porsche) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return porsche;
}
/**
* 深拷贝克隆
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Object deepClone() throws IOException, ClassNotFoundException {
//将对象写出到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流里读进来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
/**
* 保时捷车钥匙
*/
@Data
public class Key implements Cloneable {
private String keyName;
}
/**
* 测试
*/
public class Client {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//构造保时捷卡宴及车钥匙原型
Key key = new Key();
key.setKeyName("保时捷车钥匙");
Porsche porsche = new Porsche("卡宴", 1000000, "黑色",key);
System.out.println("原型车:" + porsche);
//批量生产卡宴
for (int i = 1; i <= 10; i++) {
Porsche porschei = (Porsche) porsche.clone();//浅拷贝
// Porsche porschei = (Porsche) porsche.deepClone();//深拷贝
if (porschei == porsche) {
System.out.println("原型和批量生产的车是同一个对象");
}
if (porsche.getClass()==porschei.getClass()){
System.out.println("这是生产的第" + i + "辆卡宴:" + porschei);
}
System.out.println(porsche.getKey() == porschei.getKey() ? "每辆车拥有同一把钥匙":"每辆车有各自的钥匙");
}
}
}
通过分别注释上面代码中的浅拷贝和深拷贝来测试一下结果如下:
浅拷贝:
深拷贝:
可以看出,深拷贝完成的是真正意义上的拷贝,浅拷贝拷贝后,所有车拥有的是同一把钥匙,这显然不行,10辆保时捷就一把钥匙怎么行,只能通过深拷贝去解决.
4.总结
通过上面的案例,总结下原型模式
原型模式主要包含下面三个角色:
Prototype:抽象原型类,声明克隆自身的接口,对应java.lang.Cloneable接口.
ConcretePrototype:具体原型类,实现克隆的具体操作,对应上面代码中的Porsche.
Client:客户类,客户类提出创建对象的请求,从而获得一个或多个新的对象,对应上面代码中的Client.
Object是所有类的爸爸,它提供了clone()方法,子类们可以调用父类的clone()方法进行浅拷贝,一般而言,clone()方法满足:
(1) 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象,但如果原对象中存在对其他对象的引用,该引用对象与原对象是同一个对象.
(2) 对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样.
(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立.
如果要实现深度克隆,原型及原型引用的对象均需要实现Serializable序列化接口.