原型模式的定义:
分身术说的其实就是设计模式中的原型模式。原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。听着和神仙剧的中的分身术十分相似。
如何使用原型模式:
需求:某银行举办清凉一夏抽奖活动,需要通过邮件的形式通知到客户
设计思路:邮件内容基本保持一致,主要区别是收件人。活动内容不定期改变,定义成活动模板。为了保证线程安全和消息准确性每发一次邮件就需要新建一个邮件内容类。类图设计:
代码实现:
package com.specify;
/**
* 原型模式
* 邮件类
*/
public class Mail implements Cloneable{
//收件人
private String receiver;
//邮件名称
private String subject;
//称呼
private String appllation;
//邮件内容
private String contxt;
//邮件的尾部
private String tail;
public Mail( AdvTemlate advTemlate){
this.contxt = advTemlate.getAdvContext();
this.subject = advTemlate.getAdvSubject();
}
@Override
public Mail clone(){
Mail mail = null;
try {
mail = (Mail)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return mail;
}
//get/set方法......
}
/**
* 原型模式
* 广告模板
*/
public class AdvTemlate {
//广告信名称
private String advSubject = "XX银行清凉一夏信用卡抽奖活动";
//广告信内容
private String advContext ="清凉一夏信用卡抽奖活动,只要刷卡机就有获取百万机会";
public String getAdvSubject() {
return this.advSubject;
}
public String getAdvContext() {
return this.advContext;
}
}
package com.specify;
import java.util.Random;
/**
* 原型模式
* 客户端
*/
public class Client {
//发送活动次数
private static int MAX_COUNT = 6;
public static void main(String[] args) {
//模拟发送邮件
int i = 0 ;
//定义模板
Mail mail = new Mail(new AdvTemlate());
mail.setTail("某某银行版本所有");
while (i<MAX_COUNT){
Mail cloneMail = mail.clone();
cloneMail.setAppllation(getRandString(5) + "先生(女士)");
cloneMail.setReceiver(getRandString(5) + "@"+getRandString(8));
sendMail(cloneMail);
i++;
}
}
//获得指定长度的随机字符串
public static String getRandString(int length){
String sounce = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";
StringBuffer sb = new StringBuffer();
Random random = new Random();
for (int i = 0; i <length ; i++) {
sb.append(sounce.charAt(random.nextInt(sounce.length())));
}
return sb.toString();
}
//发送邮件
public static void sendMail(Mail mail){
System.out.println("标题 : "+mail.getSubject()+"\t收件人" +mail.getReceiver()+"\t发送成功");
}
}
执行结果:
标题 : XX银行清凉一夏信用卡抽奖活动 收件人OmScx@dzmCWkkB 发送成功
标题 : XX银行清凉一夏信用卡抽奖活动 收件人PTwnk@swMvYYGt 发送成功
标题 : XX银行清凉一夏信用卡抽奖活动 收件人KaDGh@VGTUSlpu 发送成功
标题 : XX银行清凉一夏信用卡抽奖活动 收件人uWDCj@bGtwGTcn 发送成功
标题 : XX银行清凉一夏信用卡抽奖活动 收件人YOiUd@BVuvyKdE 发送成功
标题 : XX银行清凉一夏信用卡抽奖活动 收件人hNgoD@ocpKBroY 发送成功
原型模式所以然:
从代码中,我们可以看出,在生成邮件实例的过程中,使用的是重写Object的clone实现的。这也是原型模式最核心的地方。生成对象通过clone生成而不是通过new一个实例。效果是一样,但是产生对象的过程却有很大差别。clone是在内存上直接进行二进制拷贝,要比new一个对象的性能要好的多,尤其是对于需要循环产生大量对象的场景。
clone产生的对象和new实例在某些场景下还是会有一定的区别:
首先clone是区别深拷贝和浅拷贝,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。和浅拷贝相对的也就是深拷贝。在引用对象中引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。所以需要注意在拷贝对象时是否要涉及深拷贝,深拷贝可以使用将成员变量或者引用对象进行独立拷贝的方式,不过使用时一定要谨慎。参考内容:Java深拷贝和浅拷贝详解
其次是进行clone时,构造方法不会被执行,这也是和对象产生的过程有关系,如果构造函数有约束的情况下不建议适用。
最后clone和final的恩怨,final修饰的变量是无法修改的,所以clone和final关键字冲突,要使用clone方法,类的成员变量上不要增加final关键字。
原型模式总结:
性能优势:相对于new一个对象,clone存在性能上的优势,尤其是产生大量对象时;
减少约束:不执行构造函数,减少了构造函数的约束,但是同时没有了约束,有时也会是劣势;
适用场景:一个对象需要多次被修改,而且仅仅是修改其中的状态值等;或者是上面的示例代码需要产生大量对象的场景。
参考《设计模式之禅》秦小波著