设计模式之原型模式
1. 什么是原型模式
Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用Prototype模式创建的实例,具有与原型一样的数据。
换句话说原型模式就是通过复制现有实例来创建新的实例,无须知道相应类的信息。
原型模式的结构:
优缺点:
优点:
-
使用原型模式创建对象比直接new一个对象更有效,因为他是直接对内存进行拷贝,数据对象的内容就全部都有了,不需要重新创建内存区域重新实例化赋值这些操作了,这样的话速度能够快很多。
-
隐藏制造新实例的复杂性
-
重复地创建相似对象时可以考虑使用原型模式
缺点:
- 每一个类必须配备一个克隆方法
- 深层复制比较复杂
特点:
-
由原型对象自身创建目标对象。也就是说,对象创建这一动作发自原型对象本身。
-
目标对象是原型对象的一个克隆。也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
-
根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
适用场合:
-
复制对象的结构与数据
-
希望对目标对象的修改不影响既有的原型对象
-
创建对象成本较大的情况下
-
在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
-
希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)
-
隐藏克隆操作的细节。很多时候,对对象本身的克隆需要涉及到类本身的数据细节。
使用时注意事项:
-
使用原型模式复制对象不会调用类的构造方法。所以,单例模式与原型模式是冲突的,在使用时要特别注意。
-
Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
2. 具体实例:
银行的电子账单、广告信需要你群发邮件,特点:量大、时间要求紧。
想一下这个怎么实现,其实简单的想法就是邮件类,邮件模板类,发送邮件类这样三个函数,然后使用多线程给每个线程分配一个邮件的对象进行发送。这样的话的缺点就是需要实例化很多的对象,实例化对象的时候会有类的初始化赋值这一类的操作,速度慢,然后我们想就是因为每一封邮件是类似的,只是发送的人姓名什么的不同,我们直接拷贝对象,直接从内存中拷贝对象的话对象的信息都有然后重新进行修改,省下了new的整个过程,速度快很多。
设计的类图:
其实从图中看不出具体的原型模式是怎么回事。看一下具体的代码;
public class EventTemplate {
private String eventSubject, eventContent;
public EventTemplate(String eventSubject, String eventContent) {
this.eventSubject = eventSubject;
this.eventContent = eventContent;
}
public String geteventSubject() {
return eventSubject;
}
public String geteventContent() {
return eventContent;
}
}
public class Mail implements Cloneable {
private String receiver;
private String subject;
private String content;
private String tail;
private ArrayList<String> ars;
public Mail(EventTemplate et) {
this.tail = et.geteventContent();
this.subject = et.geteventSubject();
}
@Override
public Mail clone() {
Mail mail = null;
try {
mail = (Mail) super.clone();
mail.ars = (ArrayList<String>)this.ars.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return mail;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
}
Mail这个类实现了Cloneable,继承了public Mail clone() {}这个方法,用来实现对象的拷贝。
public class MainTest {
public static void main(String[] args) {
int i = 0;
int MAX_COUNT = 10;
EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
Mail mail = new Mail(et);
while (i < MAX_COUNT) {
// 以下是每封邮件不同的地方
Mail cloneMail = mail.clone();
cloneMail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..."
+ mail.getTail());
cloneMail.setReceiver(getRandString(5) + "@" + getRandString(8)
+ ".com");
// 然后发送邮件
sendMail(cloneMail);
i++;
}
}
public static String getRandString(int maxLength) {
String source = "abcdefghijklmnopqrskuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuffer sb = new StringBuffer();
Random rand = new Random();
for (int i = 0; i < maxLength; i++) {
sb.append(source.charAt(rand.nextInt(source.length())));
}
return sb.toString();
}
public static void sendMail(Mail mail) {
System.out.println("标题:" + mail.getSubject() + "\t收件人:"
+ mail.getReceiver() + "\t内容:" + mail.getContent()
+ "\t....发送成功!");
}
}
在测试类中每次发不同的邮件时都会Mail cloneMail = mail.clone();就是对象的复制。