基本定义
原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
在原型模式中,所发动创建的对象通过请求原型对象来拷贝原型对象自己来实现创建过程,当然所发动创建的对象需要知道原型对象的类型。这里也就是说所发动创建的对象只需要知道原型对象的类型就可以获得更多的原型实例对象,至于这些原型对象时如何创建的根本不需要关心。
讲到原型模式了,我们就不得不区分两个概念:深拷贝、浅拷贝。
浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝。
深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。
原型模式浅复制
package com.bjsxt.prototype;
import java.util.Date;
public class Resume implements Cloneable {
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
private String name;
private String sex;
private int age;
private Date date;
public Resume(String name, String sex, int age, Date date) {
this.name = name;
this.sex = sex;
this.age = age;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package com.bjsxt.prototype;
import java.util.Date;
/**
* 原型模式浅复制
* @author acer
*
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(2333333);
Resume s1 = new Resume("大鸟", "男", 29, date);
Resume s2 = (Resume) s1.clone();
System.out.println(s1.getDate() + "--------" + s2.getDate());
s2.setDate(new Date(1314));
System.out.println(s1.getDate() + "--------" + s2.getDate());
}
}
原型模式深复制
package com.bjsxt.prototype;
import java.util.Date;
public class Resume1 implements Cloneable {
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Resume1 resume1 = (Resume1)obj;
resume1.date = (Date) this.date.clone();
return obj;
}
private String name;
private String sex;
private int age;
private Date date;
public Resume1(String name, String sex, int age, Date date) {
this.name = name;
this.sex = sex;
this.age = age;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package com.bjsxt.prototype;
import java.util.Date;
/**
* 原型模式深复制
* @author acer
*
*/
public class Client1 {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(2333333);
Resume1 s1 = new Resume1("大鸟", "男", 29, date);
Resume1 s2 = (Resume1) s1.clone();
System.out.println(s1.getDate() + "--------" + s2.getDate());
System.out.println(s1.getName() + "--------" + s2.getName());
s2.setDate(new Date(13141314));
s2.setName("小菜");
System.out.println(s1.getDate() + "--------" + s2.getDate());
System.out.println(s1.getName() + "--------" + s2.getName());
}
}
深复制,使用序列化和反序列化的模式实现深克隆
序列化和反序列化的概念
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
package com.bjsxt.prototype;
import java.io.Serializable;
import java.util.Date;
public class Resume implements Serializable {
private String name;
private String sex;
private int age;
private Date date;
public Resume(String name, String sex, int age, Date date) {
this.name = name;
this.sex = sex;
this.age = age;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package com.bjsxt.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
/**
* 原型模式(深复制,使用序列化和反序列化的模式实现深克隆)
* @author acer
*
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException, Exception {
Date date = new Date(2333333);
Resume s1 = new Resume("大鸟", "男", 29, date);
//使用序列化和反序列化深复制
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s1);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
Resume s2 = (Resume) ois.readObject();//克隆好的对象
System.out.println(s1.getDate() + "--------" + s2.getDate());
System.out.println(s1.getName() + "--------" + s2.getName());
s2.setDate(new Date(13141313));
s2.setName("小菜");
System.out.println(s1.getDate() + "--------" + s2.getDate());
System.out.println(s1.getName() + "--------" + s2.getName());
}
}
模式优缺点
优点
1、如果创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
2、可以使用深克隆保持对象的状态。
3、原型模式提供了简化的创建结构。
缺点
1.在实现深克隆的时候可能愮比较复杂的代码。
2、需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
适用场景
1、如果创建新对象成本较大,我们可以利用已有的对象进行复制来获得。
2、如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
3、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
总结
1、原型模式向客户隐藏了创建对象的复杂性。客户只需要知道要创建对象的类型,然后通过请求就可以获得和该对象一模一样的新对象,无须知道具体的创建过程。
2、克隆分为浅克隆和深克隆两种。
3、我们虽然可以利用原型模式来获得一个新对象,但有时对象的复制可能会相当的复杂,比如深克隆。