原型模式的定义:
Specify the kinds of objects to create using a prototypical instance , and create new objects by copying this prototype .
使用原型实例指定要创建的对象类型,通过复制这个实例创建新的对象。
原型模式在生活中的应用场景:
假如你刚给自己的客厅做了装修,你朋友正好也希望给他的客厅做装修,那么,他可能把你家的装修方案拿过来改改就成,你的装修方案就是原型。
原型模式的实现:
提供一个静态工厂方法来获得原型数据,然后复制这些数据,最后做相应的初始化。为了操作安全,我们不直接使用原型数据,而是使用clone()方法从内存中克隆这条原型数据再做后续操作。在这里使用的是Java提供java.lang.Cloneable接口克隆数据。
(我们在复制新的新的数据时,需要特别注意的是,我们不能把所有数据都复制过来,例如,当对象包含主键时,不能使用原型数据的主键,必须创建一个新的主键。)
package com.alex.prototypepattern;
public class PackageInfoTestDrive {
public static void main(String[] args) {
PackageInfo currentInfo = PackageInfo.clonePackage("John");
System.out.println("Original package information:");
display(currentInfo);
currentInfo.setId(10000l);
currentInfo.setReceiverName("Ryan");
currentInfo.setReceiverAddress("People Square, Shanghai");
System.out.println("\nNew package information:");
display(currentInfo);
}
private static void display(PackageInfo currentInfo) {
System.out.println("Package id: " + currentInfo.getId());
System.out.println("Receiver name: " + currentInfo.getReceiverName());
System.out.println("Receiver address: " + currentInfo.getReceiverAddress());
System.out.println("Sender name: " + currentInfo.getSenderName());
System.out.println("Sender Phone No.: " + currentInfo.getSenderPhoneNo());
}
}
package com.alex.prototypepattern;
public class PackageInfo implements Cloneable{
private Long id;
private String receiverName;
private String receiverAddress;
private String senderName;
private String senderPhoneNo;
//other fields, getters, setters and other methods...
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getReceiverName() {
return receiverName;
}
public void setReceiverName(String receiverName) {
this.receiverName = receiverName;
}
public String getReceiverAddress() {
return receiverAddress;
}
public void setReceiverAddress(String receiverAddress) {
this.receiverAddress = receiverAddress;
}
public String getSenderName() {
return senderName;
}
public void setSenderName(String senderName) {
this.senderName = senderName;
}
public String getSenderPhoneNo() {
return senderPhoneNo;
}
public void setSenderPhoneNo(String senderPhoneNo) {
this.senderPhoneNo = senderPhoneNo;
}
public PackageInfo clone(){
try{
return (PackageInfo)super.clone();
}catch(CloneNotSupportedException ex){
System.out.println("Cloning not allowed");
return null;
}
}
public static PackageInfo clonePackage(String userName){
PackageInfo prototype = loadPackageInfo(userName);
prototype.clone();
prototype.setId(null);
return prototype;
}
//Simulate loading data from database
private static PackageInfo loadPackageInfo(String userName) {
PackageInfo packageInfo = new PackageInfo();
//create a String object in jvm heap not jvm string pool
packageInfo.setId(100l);
packageInfo.setReceiverName("John");
packageInfo.setReceiverAddress("People Square,Shanghai");
packageInfo.setSenderName("William");
packageInfo.setSenderPhoneNo("12345678901");
return packageInfo;
}
}
运行结果:
Original package information:
Package id: null
Receiver name: John
Receiver address: People Square,Shanghai
Sender name: William
Sender Phone No.: 12345678901
New package information:
Package id: 10000
Receiver name: Ryan
Receiver address: People Square, Shanghai
Sender name: William
Sender Phone No.: 12345678901
Object 的 clone()方法的复制是采用逐字节的方式从内存复制数据,复制了对象属性的引用,而属性所指的对象本身没有复制,因此所复制的引用指向了相同的对象。用这种方式复制对象是浅拷贝,不是深拷贝。
注:本文大部分内容点来自刘济华的《漫谈设计模式》