原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
案例:以邮箱发送邮件为例
邮件模板:假设模板是从数据库里查询出来的
package design.prototype;
public class AdvTemplet {
private String title;
private String content;
public AdvTemplet() {
super();
}
public AdvTemplet(String title, String content) {
super();
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
邮件对象:一定要实现Cloneable接口,这是一个标识,具有这个标识的对象才可以被拷贝
package design.prototype;
public class Mail implements Cloneable {
private String title;
private String received;
private String content;
private String addresser;
private String email_address;
public Mail(AdvTemplet advtemplet) {
super();
this.title = advtemplet.getTitle();
this.content = advtemplet.getContent();
}
@Override
protected Mail clone(){
Mail mail = null;
try {
mail = (Mail)super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return mail;
}
void sendMail(){
System.out.println("邮件:"+title+"内容:"+content+"收件人:"+received+"收件地址:"+email_address+"发送成功");
}
public Mail() {
super();
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getReceived() {
return received;
}
public void setReceived(String received) {
this.received = received;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAddresser() {
return addresser;
}
public void setAddresser(String addresser) {
this.addresser = addresser;
}
public String getEmail_address() {
return email_address;
}
public void setEmail_address(String email_address) {
this.email_address = email_address;
}
}
测试:
package design.prototype;
import java.util.Random;
public class Client {
public static void main(String[] args) {
AdvTemplet adv = new AdvTemplet("双十二大减价","双十二所有商品大减价,走过路过不要错过!!!");
Mail mail = new Mail(adv);
Mail one = mail.clone();
one.setAddresser("taobao");
setReceived(one);
one.sendMail();
System.out.println(one==mail);
}
static void setReceived(Mail one){
String received = getString(6);
one.setReceived(received);
one.setEmail_address(received+"@"+getString(6)+".com");
}
static String getString(int num){
char[] charArr = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
StringBuffer sb = new StringBuffer();
for(int i=0;i<num;i++) {
sb.append(charArr[new Random().nextInt(charArr.length)]);
}
return sb.toString();
}
}
此处只针对原型模式来讲解,一些代码上的细节设计可能不是很到位,请忽略...
假设有多条线程同时发送邮件,每条线程都去查询数据库模板显然性能会降低,而且我们还要对mail对象进行配置,不可能使用同一个mail对象,所以此处我们可以用使用原型模式,对mail对象进行克隆,再用多线程处理发送,从而提高性能
这里需要注意浅拷贝、深拷贝,浅拷贝只拷贝对象本身,如果对象的属性中包含其他对象、数组等的引用,则不会拷贝对象属性,只会拷贝其引用,引用的地址还是之前的对象、数组等地址;这种情况下必须进行深拷贝,连其属性一起拷贝;
如下
第一种关系属性对象没有再关联其他对象
package design.prototype;
import java.util.LinkedList;
public class Mail implements Cloneable {
private LinkedList list; //不能被final修饰
@Override
protected Mail clone(){
Mail mail = null;
try {
mail = (Mail)super.clone();
mail.list = (LinkedList) this.list.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return mail;
}
}
测试:
public static void main(String[] args) {
AdvTemplet adv = new AdvTemplet("双十二大减价","双十二所有商品大减价,走过路过不要错过!!!");
Mail mail = new Mail(adv);
LinkedList<String> list = new LinkedList<>();
list.add("q");
list.add("w");
list.add("e");
mail.setList(list);
Mail one = mail.clone();
one.getList().remove("e");
System.out.println(mail);
System.out.println(one);
}
测试结果
Mail [title=双十二大减价, received=null, content=双十二所有商品大减价,走过路过不要错过!!!, addresser=null, email_address=null, list=[q, w, e]]
Mail [title=双十二大减价, received=null, content=双十二所有商品大减价,走过路过不要错过!!!, addresser=null, email_address=null, list=[q, w]]
删除one中的list数据不影响mail对象的数据,克隆成功
第二种比较复杂的就得用序列化的形式把对象写进流里在读出来克隆
package design.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.LinkedList;
public class Mail implements Serializable,Cloneable{ //实现序列化接口
private LinkedList<Picture> list = new LinkedList<>();
public LinkedList<Picture> getList() {
return list;
}
public void setList(LinkedList<Picture> list) {
this.list = list;
}
@Override
protected Mail clone(){
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo;
try {
oo = new ObjectOutputStream(bo);
oo.writeObject(this);
// 从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (Mail)oi.readObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
测试
public static void main(String[] args) {
AdvTemplet adv = new AdvTemplet("双十二大减价","双十二所有商品大减价,走过路过不要错过!!!");
Mail mail = new Mail(adv);
LinkedList<Picture> list = new LinkedList<>();
Picture p1 = new Picture("地址1","size1");
Picture p2 = new Picture("地址2","size2");
Picture p3 = new Picture("地址3","size3");
list.add(p1);
list.add(p2);
list.add(p3);
mail.setList(list);
Mail one = mail.clone();
for (Picture picture : one.getList()) {
picture.setAddress("null");
}
System.out.println(mail);
System.out.println(one);
}
测试结果
Mail [title=双十二大减价, received=null, content=双十二所有商品大减价,走过路过不要错过!!!, addresser=null, email_address=null, list=[Picture [address=地址1, size=size1], Picture [address=地址2, size=size2], Picture [address=地址3, size=size3]]]
Mail [title=双十二大减价, received=null, content=双十二所有商品大减价,走过路过不要错过!!!, addresser=null, email_address=null, list=[Picture [address=null, size=size1], Picture [address=null, size=size2], Picture [address=null, size=size3]]]
把one中的list数据中的picture对象的address设置成null不影响mail对象的数据,克隆成功