从原型模式开始诉说
何为原型模式?以实际应用场景来说,例如我们需要向客户批量发送邮件时,显然我们需要去引用一个具体的邮件模板,格式类似于“亲爱的xxxx(先生)女士: xxxx银行办理信用卡优惠…”之类的,之后再通过添加各个具体信息,发送具体邮件。而这个模板就是我们说的原型。
原型模式定义
原型模式定义:用原型实例()制定创建对象的种类,并通过拷贝这些原型创建新的对象
–摘自设计模式之禅
上代码
话不多说,以上文发邮件为例。我们的原型模式代码为
邮件原型的DTO代码
package com.example.mail.代理模式;
/**
* 邮件
*/
public class Mail implements Cloneable {
//收件人
private String receiver;
//邮件名称
private String subject;
//称谓
private String application;
//邮件内容
private String contxt;
//邮件尾部版权信息
private String tail;
public Mail (AdvTemplate advTemplate){
this.contxt = advTemplate.getAdvContext();
this.subject = advTemplate.getAdvSubject();
}
@Override
public Mail clone () {
Mail mail = null;
try {
mail = (Mail) super.clone();
}catch (CloneNotSupportedException e){
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 String getApplication() {
return application;
}
public void setApplication(String application) {
this.application = application;
}
public String getContxt() {
return contxt;
}
public void setContxt(String contxt) {
this.contxt = contxt;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
}
此处为具体信内容的DTO
package com.example.mail.代理模式;
/**
* 代理模式广告信模板
*/
public class AdvTemplate {
//广告信名称
private String advSubject = "光大银行春节抽奖活动";
//广告信内容
private String advContext = "春节抽奖活动通知:一刷卡就送一百万";
public String getAdvSubject() {
return advSubject;
}
public void setAdvSubject(String advSubject) {
this.advSubject = advSubject;
}
public String getAdvContext() {
return advContext;
}
public void setAdvContext(String advContext) {
this.advContext = advContext;
}
}
主线程,执行场景
package com.example.mail.代理模式;
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 AdvTemplate());
mail.setTail("光大银行版权所有");
while (i <MAX_COUNT) {
//使用模板
Mail cloneMail = mail.clone();
cloneMail.setApplication(getRandString(5)+"先生(女士)");
cloneMail.setReceiver(getRandString(5)+"@" +getRandString(8) + ".com");
//发邮件
sendMail(cloneMail);
i++;
}
}
//随机生成字符串
public static String getRandString(int maxLength) {
String source = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
StringBuffer sb = new StringBuffer();
Random random = new Random();
for (int i=0 ; i <maxLength;i++){
sb.append(source.charAt(random.nextInt(source.length())));
}
return sb.toString();
}
public static void sendMail (Mail mail) {
System.out.println("标题: "+ mail.getSubject() + "\t收件人: " + mail.getReceiver() + "\t ...发送成功");
}
}
说明:原型模式最大的特点便是提供一个clone方法,让你去拷贝原始对象。
嗯,有的同学要是问为啥用clone,我可以这么去用对象啊
// 新手同学的写法
Mail cloneMail = new Mail();
cloneMail = mail;
这么做的小伙伴可以去试试打印下mail里的值,随着你之后对cloneMail的操作(比如赋值),你也会改变mail这个原型里的值。要是再来个多线程,那么相信我,mail里的值会被改到妈妈都不认识。而其他使用这个原型的地方也会。。。。
结论
从上述例子我们可以get到当我们需要以一个bean为模板的时候(既然是模板,那肯定是不能在模板里进行修改),我们正确的copy方式是使用clone()的方式去拷贝bean里的属性而非使用引用的方式去指向模板,牢记模板是不可以被修改的。这也是一些新手小白产生一些离奇bug的原因。当然老湿机肯定觉得我说了一大顿废话。
注意事项
clone()f方法就万事大吉了?你想偷懒,java也想偷懒,于是就尴尬了。在java中使用clone()方法时请注意java是不会将你的非基本数据类型的属性copy过来(没错,也就是bean中还有bean的,包含的bean属性不会copy过来)。List也不会
因此clone时得注意这点,如果拷贝过来,那么bean中重写clone方法的时候添加clone需要的bean属性逻辑。有点绕了,直接看代码吧。
//定义一个List无法被clone
private ArrayList<String> arrayList =new ArrayList<>();
@Override
public Mail clone () {
Mail mail = null;
try {
mail = (Mail) super.clone();
//划重点。写上clone无法被copy的属性的拷贝逻辑,此处list无法被拷贝,因此加上List的copy逻辑
this.arrayList = (ArrayList<String>) this.arrayList.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return mail;
}
顺道说下这便是所谓的深拷贝,关于深浅拷贝之间的介绍,之后我会继续更新文章详解的