定义
原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
特点
不需要知道任何创建细节,不调用构造函数。
适用场景
- 类初始化消耗较多资源。
- new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)。
- 构造函数比较复杂的时候。
- 循环体中生产大量的对象时。
优点
- 性能比new 略高
- 简化创建过程
缺点
-
必须配备克隆方法
-
对克隆复杂对象或对克隆出的对象进行改造时,容易引入危险(有深拷贝和浅拷贝相关的BUG)。
深拷贝与浅拷贝
简单点说 假设 A对象中一个属性为B,当A进行拷贝生成A1时同时A1里的属性为B1。但是当修改B时,B1跟着修改,这就是浅拷贝。
代码
这里我们就以发送邮件的逻辑为基础,做一个展示。
首先定义邮件类Mail
public class Mail implements Cloneable{
private String name;
private String content;
private String address;
//默认当前时间 由于是对象,在clone的时候会有深浅拷贝的问题
private Date sendDate = new Date();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getSendDate() {
return sendDate;
}
public void setSendDate(Date sendDate) {
this.sendDate = sendDate;
}
@Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", content='" + content + '\'' +
", address='" + address + '\'' +
", sendDate=" + sendDate +
'}' + super.toString();
}
//注意这里,会涉及到深浅拷贝的问题
@Override
protected Object clone() throws CloneNotSupportedException {
Mail cloneMail = (Mail) super.clone();
return cloneMail;
}
}
这里我故意写了一个Date
类型的属性,这样我们会更容易看出深浅拷贝BUG。
邮件发送类MailUtil
public class MailUtil {
private static String CONTENT = "向{0},邮件地址:{1},发送内容: {2},发送时间:{3},发送成功!";
public static void sendMail(Mail mail) {
System.out.println(MessageFormat.format(CONTENT,mail.getName(),mail.getAddress(),mail.getContent(),mail.getSendDate()));
}
}
我们可以看到就是简单的输出。
发送启动类
public class PrototypeBootStrap {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
Mail mail = new Mail();
mail.setName("钢铁侠");
mail.setContent("我是你的粉丝");
mail.setAddress("复仇者联盟");
Mail cloneMail = (Mail) mail.clone();
MailUtil.sendMail(mail);
MailUtil.sendMail(cloneMail);
//穿越一下
mail.getSendDate().setTime(99999999999L);
MailUtil.sendMail(mail);
MailUtil.sendMail(cloneMail);
}
}
我们看到 在第一次发送邮件之后,不小心穿越一下,设置了原对象(mail
)的时间。正常我们理解既然cloneMail
是mail
的拷贝。那么原对象修改,拷贝的对象里的值是不会变得。但是结果是这样吗?我们运行一下。
这里我们就发现了浅拷贝的问题了。也是BUG率最容易出现的地方。
改进邮件类Mail
我们将邮件类中的 clone()
方法改进一下。
@Override
protected Object clone() throws CloneNotSupportedException {
Mail cloneMail = (Mail) super.clone();
//避免浅拷贝
cloneMail.sendDate = (Date) cloneMail.sendDate.clone();
return cloneMail;
}
我们再来运行一下启动类。
prototypecopy1.jpg这次的输出结果却是是我们想要的。
小结
在运用原型模式的时候,一定要多加小心,避免不必要的麻烦。希望我们可以在设计模式的海洋里找到属于自己的那片蓝。
网友评论