模式定义
原型模式(Prototype Pattern):原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节。原型模式的基本工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象复制原型来实现创建过程。
模式结构
1.Prototype
抽象原型类是定义具有克隆自己方法的接口,是所有具有原型类的公共父类,可以抽象类也可以是接口。
2.ConcretePrototype
具体原型类实现具体的克隆方法,在克隆方法中返回自己的一个克隆对象。
3.Client
让一个原型克隆自身,从而创建一个新的对象,在客户类中只需直接实例化或通过工厂方法等方式创建一个对象,再通过调用该对象的克隆方法复制得到多个相同的对象。
对于原型模式的使用需要注意一下两个问题:
- 深克隆与浅克隆
在通常情况下,一个类包含一些成员对象,在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆和浅克隆;
(1)浅克隆
在浅克隆中,被复制对象的所有普通成员变量都具有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅克隆仅仅复制所考虑的对象,而不复制它所引用的成员对象,如下图所示:
obj1为原型对象,obj2为复制后的对象,containedObj1和containedObj2为成员对象。
(2)深克隆
在深克隆中被复制的对象的所有普通成员变量都具有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深克隆把要复制的对象所引用的对象都复制了一遍。如下图所示:
Java语言原型模式的实现
Java语言提供的clone()方法将对象复制一份返回给调用者。一般而言,clone()方法满足:- 对任何对象x,都有x.clone() != x,即克隆对象与原对象不是同一个对象。
- 对任何对象x,都有x.clone().getClass() == x.getClass(), 即克隆对象与原对象的类型一样。
- 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法,具体步骤如下:
- 在派生类中覆盖基类的clone()方法,并声明为public。
- 在派生类的clone()方法中,调用super.clone()。
- 在派生类中实现Cloneable接口。
在Java语言中,序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制引用的成员对象,因此通过序列化将对象写到一个流中,再从流中将其读出,从而实现深克隆。
实例 ——浅克隆
由于邮件对象包含的内容较多(如发送者、接受者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,对于已经创建好的邮件对象,可以通过复制的方式创建一个新的邮件对象,如果需要改变某部分内容,无须修改原始的邮件对象,只需要修改复制后得到的邮件对象即可。使用原型模式设计本系统,在本实例中使用浅克隆实现邮件复制,即复制邮件的同时不复制附件。
类图如下所示:
public class Email implements Cloneable {
private Attachment attachment;
public Email() {
this.attachment = new Attachment();
}
public Object clone() {
Email copy = null;
try {
copy = (Email)super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Clone Failure");
}
return copy;
}
public Attachment getAttachment() {
return this.Attachment;
}
public void display() {
System.out.println("查看邮件");
}
}
// 附件类
public class Attachment {
public void download() {
System.out.println("下载附件");
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
Email email, copyEmail;
email = new Email();
copyEmail = (Email)email.clone();
System.out.println("email == copyEmail ?");
System.out.println(email == copyEmail);
System.out.println("email.getAttachment == copyEmail.getAttachment ?");
System.out.println(email.getAttachment() == copyEmail.getAttachment());
}
}
输出结果为:
email == copyEmail ?
false
email.getAttachment == copyEmail.getAttachment ?
true
实例——深克隆
仍然实现上面的例子,这次使用深克隆。
import java.io.*;
public class Email implements Serializable {
private Attachment attachment;
public Email() {
this.attachment = new Attachment();
}
public Object deepClone()
throws IOException, ClassNotFoundException, OptionalDataException {
// 将对象写入流中
ObjectOutputStream os = new ObjectOutputStream(
new ByteArrayOutputStream());
os.writeObject(this);
// 将对象从流中取出
ObjectInputStream is = new ObjectInputStream(
new ByteArrayInputStream());
retuen is.readObject();
}
public Attachment getAttachment() {
return this.attachment;
}
public void display() {
System.out.println("查看邮件");
}
}
// 附件类
public class Attachment implements Serializable {
public void download() {
System.out.println("下载邮件");
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
Email email, copyEmail;
email = new Email();
copyEmail = (Email)email.clone();
System.out.println("email == copyEmail ?");
System.out.println(email == copyEmail);
System.out.println("email.getAttachment == copyEmail.getAttachment ?");
System.out.println(email.getAttachment() == copyEmail.getAttachment());
}
}
输出结果为:
email == copyEmail ?
false
email.getAttachment == copyEmail.getAttachment ?
false