原型模式
- 定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- 不需要知道任何创建的细节,不需要调用构造函数
- 类型:创建型
适用的场景
- 类初始化消耗较多资源(比较重的对象)
- new产生的一个对象需要非常频繁的过程(数据准备、访问权限等等)
- 构造函数比较复杂
- 循环体中生产大量的对象时
另外在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
优点与缺点
优点:
- 原型模式性能比直接new一个对象性能搞
- 简化创建过程
clone
通过复制对象的二进制流的方式来复制一个对象,因此免去了new对象时比较繁重的一些操作。
但克隆的方式有时会留下坑,这就是深克隆与浅克隆的区别,后面会举例子说明。
浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
缺点:
- 必须配备克隆方法(java cloneable接口)
- 对克隆复杂对象或者对克隆出的对象进行复杂改造,容易引入风险。
代码实现原型模式
要让对象具备克隆的能力,需要让类实现Cloneable
接口,并重写Object
类的clone
方法。
public class Mail implements Cloneable {
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("clone mail object");
return super.clone();
}
}
public class MailUtil {
public static void sendMail(Mail mail) {
String outputContent = "向{0}同学,邮件地址{1},邮件内容:{2}发送邮件。";
System.out.println(MessageFormat.format(outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()));
}
public static void saveOriginMail(Mail mail) {
System.out.println("存储originMail记录,origin Mail:"+mail.getContent());
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("初始化模板");
for (int i = 0; i < 10; i++) {
Mail tmp = (Mail) mail.clone();
mail.setName("姓名"+i);
mail.setEmailAddress("姓名"+i+"@qq.com");
mail.setContent("恭喜你,中奖了!");
MailUtil.sendMail(tmp);
}
MailUtil.saveOriginMail(mail);
}
以上就完成一个简单的邮件克隆的demo。后面这个例子会演示浅克隆以及深克隆对程序的影响。
深克隆与浅克隆
public class Pig implements Cloneable {
private String name;
private Date birthday;
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Date birthday = new Date(0L);
Pig pig1 = new Pig("佩奇", birthday);
Pig pig2 = (Pig) pig1.clone();
birthday.setTime(System.currentTimeMillis());
System.out.println("pig1 = " + pig1);
System.out.println("pig2 = " + pig2);
}
如果我们的本意是,希望复制出来的对象是在内存中独立的两个对象。那上面的这个克隆就出大问题了。很明显这就是一个浅克隆带来的问题。
问题就处在Pig类里的Date,这是个引用类型,在浅拷贝中,并不会对指针所指向的对象进行复制,而是复制了引用,实际的对象还是原来的那个对象。
我们可以使用debug追踪一下:
很明显,birthday引用指向的都是同一个对象。深拷贝也很简单,就是对引用类型也进行一次克隆:
@Override
protected Object clone() throws CloneNotSupportedException {
Pig pig = (Pig) super.clone();
Date date = (Date) pig.getBirthday().clone();
pig.setBirthday(date);
return pig;
}
在使用原型模式,要合理地使用深拷贝和浅拷贝,避免踩坑!!!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)