源代码
GitHub源代码
1.本文目标
本文目标是为了让大家认识并理解原型模式。
2.基本套路
定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
类型:创建型
选择关键点:创建出来的对象是否可以立即投入使用
设计原则:无
使用概率:10%
难度系数:中低
3.适用场景
1.类初始化消耗较多资源。
2.new产生的一个对象需要非常繁琐的过程(属性赋值,访问权限等)。
3.构造函数比较复杂
4.循环体中生产大量对象
4.使用步骤
1.创建具体类实现Cloneable接口
2.重写clone方法
5.举个栗子
我们用具体的代码去更好的理解这个设计模式
5.1浅拷贝栗子说明
- 背景:需要给中奖的同学发送邮件
- 目的:希望使用原型模式生成对象并发送邮件
5.2浅拷贝使用步骤
实现代码如下:
步骤1.创建Mail类,实现Cloneable共接口
步骤2.重写clone 方法
public class Mail implements Cloneable{
private String mName;
private String mEmailAddress;
private String mContent;
public Mail() {
System.out.println("无参构造方法");
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
public String getEmailAddress() {
return mEmailAddress;
}
public void setEmailAddress(String mEmailAddress) {
this.mEmailAddress = mEmailAddress;
}
public String getContent() {
return mContent;
}
public void setContent(String mContent) {
this.mContent = mContent;
}
@Override
public String toString() {
return "Mail{" +
"mName='" + mName + '\'' +
", mEmailAddress='" + mEmailAddress + '\'' +
", mContent='" + mContent + '\'' +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("重写Mail对象的clone方法");
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 saveOriginMailRecord(Mail mail){
System.out.println("存储originMail记录,originMail:"+mail.getContent());
}
}
测试方法:
public static void main(String[] args) {
/**
* 下面这个例子目的是:
* 创建一个Mail对象,然后保存初始化的内容,然后一个for循环,发送10份出去
*/
Mail mail = new Mail();
mail.setContent("初始化模板");
System.out.println("初始化mail:"+mail);
for(int i = 0;i < 10;i++){
//根据原始的Mai对象,去clone 一个全新的Mail对象,这个是用二进制的流的方式复制的,所以性能比较好
//目前这个是浅拷贝
Mail mailTemp = (Mail) mail.clone();//这个clone并没有调用构造器,只是调用了clone方法,但是对象是新的
mailTemp.setName("姓名"+i);
mailTemp.setEmailAddress("姓名"+i+"@163.com");
mailTemp.setContent("恭喜您,你中奖了,500W!!!!");
MailUtil.sendMail(mailTemp);//发送邮件
System.out.println("克隆的mailTemp:"+mailTemp);
}
//假如现在的业务非常复杂,这个保存Mail对象需要放到最后面
//保存原始的Mail对象
MailUtil.saveOriginMailRecord(mail);
}
5.3深拷贝使用步骤
实现代码如下:
步骤1.创建Pig类,实现Cloneable共接口
步骤2.重写clone 方法,在该方法中进行一些操作
public class Pig implements Cloneable{
private String mName;
private Date mBirthday;
public Pig(String name, Date birthday) {
this.mName = name;
this.mBirthday = birthday;
}
public String getName() {
return mName;
}
public void setName(String name) {
this.mName = name;
}
public Date getBirthday() {
return mBirthday;
}
public void setBirthday(Date birthday) {
this.mBirthday = birthday;
}
@Override
public String toString() {
return "Pig{" +
"name='" + mName + '\'' +
", birthday=" + mBirthday +
'}'+super.toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
//深克隆
Pig pig = (Pig)super.clone();
pig.mBirthday = (Date) pig.mBirthday.clone();
return pig;
}
}
测试方法:
public static void main(String[] args) {
Date birthday = new Date(0L);
Pig pig1 = new Pig("佩奇",birthday);
Pig pig2 = (Pig) pig1.clone();//从pig1 clone出来pig2
/**
* 下面的打印pig1和pig2是两个不同的对象,这个没有问题
* 但是,里面的Date类型的mBirthday生日对象,这个是同一个对象,这个克隆出来的Date引用的是用一个对象
* 所以修改pig1的生日,pig2也会跟着改变(这就是需要注意的点),所以需要深克隆
*/
System.out.println(pig1);
System.out.println(pig2);
//改变pig1的生日日期,目的只是修改pig1的生日,并没有打算修改pig2的生日
pig1.getBirthday().setTime(666666666666L);
/**
* 如果不在pig对象中的clone方法写下面这一句
* pig.mBirthday = (Date) pig.mBirthday.clone();
* 就会出现问题(pig2也跟着改变时间了),所以必须要深克隆,就是加上上面的代码
*/
System.out.println(pig1);
System.out.println(pig2);
}
6.优点
- 原型模式性能比直接new一个对象性能高
- 简化创建过程
7.缺点
- 必须配备克隆方法(这个模式的核心就是clone方法)
- 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
- 深拷贝,浅拷贝要运用得当
8.总结
本文只是对原型模式进行一个分享,接下来会从创建型模式,结构型模式,行为型模式,这三大类展开一个系列分享,大家可以持续进行关注,信仰年輕的设计模式,蟹蟹啦。