概念
-
定义
用原型实例指定创建对象的种类,并且通过拷贝折现原型创建新的对象。
-
优点
-
性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个镀锌性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
-
逃避构造函数的约束
直接在内存中进行拷贝,是不会执行构造函数的。
-
-
缺点
- 必须配备克隆方法
- 对克隆复杂对象或者对克隆出的对象进行复杂的改造时,容易引入风险
- 深拷贝、浅拷贝要运用的得当
-
使用场景
-
资源优化场景
类初始化需要消耗很多资源
-
性能和安全的要求场景
通过new产生一个对象需要非常频繁的数据准备或者访问权限,则可以使用原型模式。
-
一个对象多个修改者
-
-
扩展
- 深克隆
- 浅克隆
案例
原型模式的通用模板
public class Mail {
// 覆写父类Object的方法
@Override
protected Mail clone() throws CloneNotSupportedException {
Mail mail = null;
mail = (Mail) super.clone();
return mail;
}
}
coding 使用原型模式场景
场景:有一个业务需要给没一个用户发送一个邮件,但是每个客户的邮件内容是不一样的(比如称呼),如果使用一般的情况,那么我们需要for循环变量每一个用户的时候新建一个mail对象。这个时候比较浪费资源,所有采用原型模式进行拷贝。
代码:
Mail.java
package com.dsdj.pattern.creational.prototype;
/**
* @ClassName Mail
* @Description TODO
* @Author dsdj
* @Date 2018/11/3 下午9:03
* @Version 1.0
**/
public class Mail {
/**
* 名字
*/
private String name;
/**
* 地址
*/
private String emialAddress;
/**
* 内容
*/
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmialAddress() {
return emialAddress;
}
public void setEmialAddress(String emialAddress) {
this.emialAddress = emialAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
// 覆写父类Object的方法
@Override
protected Mail clone() throws CloneNotSupportedException {
Mail mail = null;
mail = (Mail) super.clone();
return mail;
}
@Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emialAddress='" + emialAddress + '\'' +
", content='" + content + '\'' +
'}'+super.toString();
}
}
MailUtil.java
public class MailUtil {
public static void sendMail(Mail mail){
String content = "{0}客户 你好, 邮件地址:{1}, 正文:{2}发送邮件成功";
System.out.println(MessageFormat.format(content,mail.getName(),mail.getEmialAddress()));
}
public static void saveOriginMailRecord(Mail mail){
System.out.println("存储邮件模板====>"+mail.getContent());
}
}
- 不使用原型模式
@org.junit.Test
public void test01(){
Mail mail = new Mail();
mail.setContent("基本模板");
// 对每个客户发送邮件
for (int i = 0; i<10;i++){
Mail mail1 = new Mail();
mail1.setName("客户"+i);
mail1.setEmialAddress("地址"+i);
// 通用的内容
mail1.setContent(mail.getContent());
MailUtil.sendMail(mail1);
}
// 所有客户发送完成后,需要记录发送邮件的内容
MailUtil.saveOriginMailRecord(mail);
}
- 使用原型模式
@org.junit.Test
public void test02() throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("基本模板");
// 对每个客户发送邮件
for (int i = 0; i<10;i++){
Mail mail1 = mail.clone();
mail1.setName("客户"+i);
mail1.setEmialAddress("地址"+i);
// 通用的内容
mail1.setContent(mail.getContent());
MailUtil.sendMail(mail1);
}
// 所有客户发送完成后,需要记录发送邮件的内容
MailUtil.saveOriginMailRecord(mail);
}
使用原型模式可以降低new的次数,减少资源的使用。
浅拷贝和深拷贝
我们知道拷贝的对象和原对象是引用地址是不同的。但是对象里的属性值是一致的,但如果属性对象是引用对象的时候其引用地址是否一致呢?
这就引出了浅拷贝和深拷贝。
Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。
深拷贝的写法
package com.dsdj.pattern.creational.prototype.deepClone;
import java.util.Date;
public class TestMail implements Cloneable{
private String name;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "TestMail{" +
"name='" + name + '\'' +
", date=" + date +
'}'+super.toString();
}
@Override
protected TestMail clone() throws CloneNotSupportedException {
TestMail testMail = (TestMail) super.clone();
// 深拷贝
testMail.date = (Date) testMail.date.clone();
return testMail;
}
}
测试
@org.junit.Test
public void test01() throws CloneNotSupportedException {
TestMail testMail = new TestMail();
testMail.setDate(new Date(0L));
TestMail testMail1 = testMail.clone();
testMail1.setDate(new Date(999888888889L));
System.out.println(testMail.toString());// date=Thu Jan 01 08:00:00 CST 1970
System.out.println(testMail1.toString()); // date=Sat Sep 08 02:54:48 CST 2001
}