一、定义:
用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象。
通俗理解:不通过new关键字来产生一个对象, 而是通过对象复制来实现的模式就叫做原型模式。
二、类图:
三、代码:
MailTemplate.class
/**
* 邮件模板类,定义邮件中的公共属性
*/
public class MailTemplate {
private String title="xx银行国庆信用卡抽奖活动";//标题
private String appelcation="先生(女士)";//称谓
private String content="国庆抽奖活动通知:只要刷卡就送100万...";//内容
private String tail="xx银行版权所有";//尾部
public String getTitle() {
return this.title;
}
public String getAppelcation(){
return this.appelcation;
}
public String getContent(){
return this.content;
}
public String getTail() {
return this.tail;
}
}
Mail.class
/**
* 邮件类,定义一封邮件的所有属性.实现Cloneable接口,复写clone方法。
*/
public class Mail implements Cloneable{
private String receiver;//邮件接收者
private String appelcation;//称谓:女士/先生
private String title;//邮件名称
private String content;//邮件内容
private String tail;//邮件末尾:XXX版权所有
public Mail(MailTemplate template){//公共属性从模板类中获取
if(template!=null){
this.title=template.getTitle();
this.appelcation=template.getAppelcation();
this.content=template.getContent();
this.tail=template.getTail();
}
}
@Override
protected Object clone(){
// TODO Auto-generated method stub
Mail mail=null;
try {
mail=(Mail) super.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 getAppelcation() {
return appelcation;
}
public void setAppelcation(String appelcation) {
this.appelcation = appelcation;
}
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;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
}
Client.class
/**
* 场景类,发送邮件
*/
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
int MAX_COUNT=5;//发送邮件的最大数量
Mail mail=new Mail(new MailTemplate());//新建一个Mail实例,此实例含有公共的属性
int i=0;
while(i<MAX_COUNT){
Mail cloneMail=(Mail) mail.clone();//通过克隆方式一个新的Mail实例
cloneMail.setReceiver(getRandomStr(5)+"@"+getRandomStr(8)+".com");//改变mail实例的个性部分
sendMail(cloneMail);
i++;
}
}
/**
* 发送邮件
*/
private static void sendMail(Mail mail){
System.out.println("标题:"+mail.getTitle()+"\t内容:"+mail.getContent()+"\t收件人:"+mail.getReceiver()+"\t"+mail.getAppelcation()+"\t"+mail.getTail()+"\t发送成功...");
}
/**
* 获取一串随机字符
*/
private static String getRandomStr(int count){
StringBuilder strBuilder=new StringBuilder();
String origin="agakgahgadhgjkkhgauoiqrhiagnaigqergk";
Random random=new Random();
int i=0;
while(i<count){
strBuilder.append(origin.charAt(random.nextInt(origin.length())));
i++;
}
return strBuilder.toString();
}
}
注解:原型模式的核心是一个clone方法, 通过该方法进行对象的拷贝, Java提供了一个Cloneable接口来标示这个对象是可拷贝的, 为什么说是“标示”呢? 翻开JDK的帮助看看Cloneable是一个方法都没有的, 这个接口只是一个标记作用, 在JVM中具有这个标记
的对象才有可能被拷贝。 那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢? 方法是覆盖clone()方法, 是的, 你没有看错是重写clone()方法, 看看我们上面Mail类中的clone方法。
四、优点:
● 性能优良
原型模式是在内存二进制流的拷贝, 要比直接new一个对象性能好很多, 特别是要在一个循环体内产生大量的对象时, 原型模式可以更好地体现其优点。
● 逃避构造函数的约束
这既是它的优点也是缺点, 直接在内存中拷贝, 构造函数是不会执行的 。 优点就是减少了约束, 缺点也是减少了约束, 需要大家在实际应用时考虑。
五、使用场景:
● 资源优化场景
类初始化需要消化非常多的资源, 这个资源包括数据、 硬件资源等。
● 性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限, 则可以使用原型模式。
● 一个对象多个修改者的场景
一个对象需要提供给其他对象访问, 而且各个调用者可能都需要修改其值时, 可以考虑使用原型模式拷贝多个对象供调用者使用。
六、注意事项:
1.深拷贝,浅拷贝
●Object类提供的方法clone只是拷贝本对象, 其对象内部的数组、 引用对象等都不拷贝, 还是指向原生对象的内部元素地址, 这种拷贝就叫做浅拷贝。(使用原型模式时, 引用的成员变量必须满足两个条件才不会被拷贝: 一是类的成员变量, 而不是方法内变量; 二是必须是一个可变的引用对象, 而不是一个原始类型或不可变对象。)若要实现深拷贝,则需要针对每个变量再分别拷贝,如下代码所示:
深拷贝:
/**
* 邮件类,定义一封邮件的所有属性.实现Cloneable接口,复写clone方法。
*/
public class Mail implements Cloneable{
private String receiver;//邮件接收者
private String appelcation;//称谓:女士/先生
private String title;//邮件名称
private String content;//邮件内容
private String tail;//邮件末尾:XXX版权所有
private ArrayList<String> arrayList=new ArrayList<String>(){};
public Mail(MailTemplate template){//公共属性从模板类中获取
if(template!=null){
this.title=template.getTitle();
this.appelcation=template.getAppelcation();
this.content=template.getContent();
this.tail=template.getTail();
}
}
@Override
protected Object clone(){
// TODO Auto-generated method stub
Mail mail=null;
try {
mail=(Mail) super.clone();
mail.arrayList=(ArrayList<String>) this.arrayList.clone();//Object的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 getAppelcation() {
return appelcation;
}
public void setAppelcation(String appelcation) {
this.appelcation = appelcation;
}
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;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
}
这样就实现了完全的拷贝, 两个对象之间没有任何的瓜葛了, 你修改你的, 我修改我的, 不相互影响, 这种拷贝就叫做深拷贝。
●clone()方法与final关键字冲突(clone()方法要求对对象重新赋值,而final关键字不允许再修改值。),有final关键字修饰的成员变量就不能调用clone()方法,否则会报错。