Java设计模式之原型模式
//首先新建一个类Mail
public class Mail {
//这个类总共有三个属性
private String name;
private String emailAddress;
private String content;
public Mail(){
System.out.println("Mail class constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emailAddress='" + emailAddress + '\'' +
", content='" + content + '\'' +
'}';
}
}
//新建另外一个类
public class MailUtil {
public static void sendMail(Mail mail){
String content="{0}同学,邮件地址:{1},内容:{2}";
System.out.println(MessageFormat.format(content,mail.getName()
,mail.getEmailAddress(),mail.getContent()));
}
public static void saveOriginalContent(Mail mail){
System.out.println("保存原始内容:"+mail.getContent());
}
}
//调用
public static void main(String[]a){
Mail mail=new Mail();
mail.setContent("初始化模板");
for (int i=0;i<10;i++){
mail.setName("姓名"+i);
mail.setEmailAddress("邮箱"+i);
mail.setContent("邮件内容。。。");
MailUtil.sendMail(mail);
}
MailUtil.saveOriginalContent(mail);
}
//结果
Mail class constructor
姓名0同学,邮件地址:邮箱0,内容:邮件内容。。。
姓名1同学,邮件地址:邮箱1,内容:邮件内容。。。
姓名2同学,邮件地址:邮箱2,内容:邮件内容。。。
姓名3同学,邮件地址:邮箱3,内容:邮件内容。。。
姓名4同学,邮件地址:邮箱4,内容:邮件内容。。。
姓名5同学,邮件地址:邮箱5,内容:邮件内容。。。
姓名6同学,邮件地址:邮箱6,内容:邮件内容。。。
姓名7同学,邮件地址:邮箱7,内容:邮件内容。。。
姓名8同学,邮件地址:邮箱8,内容:邮件内容。。。
姓名9同学,邮件地址:邮箱9,内容:邮件内容。。。
保存原始内容:邮件内容。。。
这里有两个问题,第一是MailUtil.saveOriginalContent(mail)方法应该是保存原始内容“初始化模板”而不是for循环里面最后被赋值了。第二个问题是考虑实例化Mail对象比较消耗资源的问题。
下面看看引入原型模式后的优雅实现方法。
//Mail类实现Cloneable接口
public class Mail implements Cloneable{
private String name;
private String emailAddress;
private String content;
public Mail(){
System.out.println("Mail class constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emailAddress='" + emailAddress + '\'' +
", content='" + content + '\'' +
'}'+super.toString();
}
//重写此方法
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("clone 方法");
return super.clone();
}
}
public class MailUtil {
public static void sendMail(Mail mail){
}
public static void saveOriginalContent(Mail mail){
System.out.println("保存原始内容:"+mail.getContent());
}
}
//调用
public static void main(String[]a) throws CloneNotSupportedException {
Mail mail=new Mail();
mail.setContent("初始化模板");
System.out.println("初始化的mail对象:"+mail);
for (int i=0;i<10;i++){
Mail tmpMail= (Mail) mail.clone();
tmpMail.setName("姓名"+i);
tmpMail.setEmailAddress("邮箱"+i);
tmpMail.setContent("邮件内容。。。");
MailUtil.sendMail(tmpMail);
System.out.println("克隆的mail对象:"+tmpMail);
}
MailUtil.saveOriginalContent(mail);
}
//结果:@符号后面的地址不同表示实例化的mail对象不同.
Mail class constructor
初始化的mail对象:Mail{name='null', emailAddress='null', content='初始化模板'}com.zk.javatest.prototype.Mail@1540e19d
clone 方法
克隆的mail对象:Mail{name='姓名0', emailAddress='邮箱0', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@677327b6
clone 方法
克隆的mail对象:Mail{name='姓名1', emailAddress='邮箱1', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@14ae5a5
clone 方法
克隆的mail对象:Mail{name='姓名2', emailAddress='邮箱2', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@7f31245a
clone 方法
克隆的mail对象:Mail{name='姓名3', emailAddress='邮箱3', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@6d6f6e28
clone 方法
克隆的mail对象:Mail{name='姓名4', emailAddress='邮箱4', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@135fbaa4
clone 方法
克隆的mail对象:Mail{name='姓名5', emailAddress='邮箱5', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@45ee12a7
clone 方法
克隆的mail对象:Mail{name='姓名6', emailAddress='邮箱6', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@330bedb4
clone 方法
克隆的mail对象:Mail{name='姓名7', emailAddress='邮箱7', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@2503dbd3
clone 方法
克隆的mail对象:Mail{name='姓名8', emailAddress='邮箱8', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@4b67cf4d
clone 方法
克隆的mail对象:Mail{name='姓名9', emailAddress='邮箱9', content='邮件内容。。。'}com.zk.javatest.prototype.Mail@7ea987ac
保存原始内容:初始化模板
原型模式中的浅克隆与深克隆
先来看看浅克隆
//新建类Pig
public class Pig implements Cloneable{
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Pig{" + "name='" + name + '\''
+ ", birthday=" + birthday + '}'+super.toString();
}
}
//调用
public static void main(String[] a) throws CloneNotSupportedException {
Date birthday=new Date(0L);
Pig pig1=new Pig("佩奇",birthday);
Pig pig2= (Pig) pig1.clone();
System.out.println(pig1);
System.out.println(pig2);
pig1.getBirthday().setTime(666666666666L);
System.out.println("修改后:"+pig1);
System.out.println("修改后:"+pig2);
}
//结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@4c75cab9
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@1ef7fe8e
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@4c75cab9
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@1ef7fe8e
从运行结果可以看出,修改了pig1对象的birthday属性,pig2对象的birthday属性也被修改了。看看下面这张调试过程的图。
虽然pig1与pig1克隆出的对象pig2是两个不同的对象,但是对象里面的birthday属性是引用的是同一个对象。因此也就看到了上面的两个birthday都被修改了。
这就是浅克隆。
下面看看深克隆:修改clone方法。
public class Pig implements Cloneable{
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//深克隆
Pig pig= (Pig) super.clone();
//将引用对象Date也克隆
pig.birthday=(Date) pig.birthday.clone();
return pig;
}
@Override
public String toString() {
return "Pig{" + "name='" + name + '\''
+ ", birthday=" + birthday + '}'+super.toString();
}
}
//运行结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk..clone.Pig@6d6f6e28
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk..clone.Pig@135fbaa4
修改后:Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.zk.clone.Pig@6d6f6e28
修改后:Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.zk.clone.Pig@135fbaa4
此时修改pig1对象的birthday属性不会影响pig2的birthday属性。
因此,使用原型模式时,一定要注意浅克隆与深克隆,否则很容易导致bug。
原型模式破坏单例模式
//饿汉式单例模式,实现Cloneable接口
public class HungrySingleton implements Serializable,Cloneable{
private HungrySingleton(){
if (hungrySingleton!=null){
throw new RuntimeException("单例模式的构造器禁止反射");
}
}
private final static HungrySingleton hungrySingleton=new HungrySingleton();
public static HungrySingleton getInstance() {
return hungrySingleton;
}
private Object readResolve(){
return hungrySingleton;
}
//重写此方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//调用
HungrySingleton hungrySingleton=HungrySingleton.getInstance();
Method method=hungrySingleton.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
HungrySingleton cloneHungrySingleton= (HungrySingleton) method.invoke(hungrySingleton);
System.out.println(hungrySingleton);
System.out.println(cloneHungrySingleton);
//结果
com.zk.javatest.prototype.HungrySingleton@677327b6
com.zk.javatest.prototype.HungrySingleton@14ae5a5
从运行结果可以看出,两个实例对象不相同,单例模式被破坏。那如果防止这种破坏呢?很简单,重写clone()方法即可。
@Override
protected Object clone() throws CloneNotSupportedException {
return getInstance();
}