飞道的博客

设计模式——原型模式

287人阅读  评论(0)

原型模式

原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式的核心是一个clone()方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的。JDK中Cloneable接口只是一个标记作用,不需要实现任何方法,在JVM中具有这个标记的对象才有可能被拷贝。此时,我们可以通过覆盖和重写clone()方法将“有可能被拷贝”转换为“可以被拷贝”。

原型模式通用源码:


  
  1. public class PrototypeClass implements Cloneable{
  2. //覆写父类Object方法
  3. @Override
  4. public PrototypeClass clone(){
  5. PrototypeClass prototypeClass = null;
  6. try {
  7. prototypeClass = (PrototypeClass) super.clone();
  8. } catch (CloneNotSupportedException e) {
  9. //异常处理
  10. }
  11. return prototypeClass;
  12. }
  13. }

实现一个接口,然后重写clone方法,就完成了原型模式


原型模式的优点

  • 性能优良:原型模式是在内存二进制流的拷贝,比直接new一个对象性能好很多,特别是要在循环体内产生大量的对象时。
  • 逃避构造函数的约束:直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束。

原型模式的使用场景

  • 资源优化场景:类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
  • 性能和安全要求的场景:通过new产生一个对象需要非常繁琐的数据准备或访问权限。
  • 一个对象多个修改者的场景:一个对象需要提供给多个对象访问,且各调用者可能都需要修改其值时。

在实际项目中,原型模式很少单独出现,一般和工厂方法模式一起出现,通过clone()方法创建一个对象,然后由工厂方法提供给调用者。


原型模式的注意事项

(1)构造函数不会被执行

一个实现了Cloneable并重写了clone()方法的类A,有一个无参构造或有参构造B,通过new关键字产生了一个对象S,再然后通过S.clone()方式产生了一个新的对象T,那么在对象拷贝时构造函数B是不会被执行的。

(2)浅拷贝和深拷贝

★浅拷贝


  
  1. import java.util.ArrayList;
  2. public class Thing implements Cloneable {
  3. //定义一个私有变量
  4. private ArrayList<String> arrayList = new ArrayList<String>();
  5. @Override
  6. public Thing clone(){
  7. Thing thing = null;
  8. try {
  9. thing = (Thing) super.clone();
  10. } catch (CloneNotSupportedException e) {
  11. e.printStackTrace();
  12. }
  13. return thing;
  14. }
  15. //设置HashMap的值
  16. public void setValue(String value){
  17. this.arrayList.add(value);
  18. }
  19. //取得arrayList的值
  20. public ArrayList<String> getValue(){
  21. return this.arrayList;
  22. }
  23. }

在Thing类中增加一个私有变量arrayList,类型为ArrayList,然后通过setValue和getValue 分别进行设置和取值。

浅拷贝测试:


  
  1. import com.sfq.action.Thing;
  2. public class Client {
  3. public static void main(String[] args) {
  4. //产生一个对象
  5. Thing thing = new Thing();
  6. //设置一个值
  7. thing.setValue( "张三");
  8. //拷贝一个对象
  9. Thing cloneThing = thing.clone();
  10. cloneThing.setValue( "李四");
  11. System.out.println(thing.getValue());
  12. }
  13. }
  14. 结果
  15. [张三, 李四]

Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。两个对象共享一个私有变量,大家都能改,是一种非常不安全的方式,在实际项目中使用还是比较少的。

注意:使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可 变对象。

★深拷贝


  
  1. import java.util.ArrayList;
  2. public class Thing implements Cloneable {
  3. //定义一个私有变量
  4. private ArrayList<String> arrayList = new ArrayList<String>();
  5. @Override
  6. public Thing clone(){
  7. Thing thing = null;
  8. try {
  9. thing = (Thing) super.clone();
  10. thing.arrayList = (ArrayList<String>) this.arrayList.clone();
  11. } catch (CloneNotSupportedException e) {
  12. e.printStackTrace();
  13. }
  14. return thing;
  15. }
  16. //设置HashMap的值
  17. public void setValue(String value){
  18. this.arrayList.add(value);
  19. }
  20. //取得arrayList的值
  21. public ArrayList<String> getValue(){
  22. return this.arrayList;
  23. }
  24. }

对私有的类变量进行独立的拷贝。


  
  1. import com.sfq.action.Thing;
  2. public class Client {
  3. public static void main(String[] args) {
  4. //产生一个对象
  5. Thing thing = new Thing();
  6. //设置一个值
  7. thing.setValue( "张三");
  8. //拷贝一个对象
  9. Thing cloneThing = thing.clone();
  10. cloneThing.setValue( "李四");
  11. System.out.println(thing.getValue());
  12. System.out.println(cloneThing.getValue());
  13. }
  14. }
  15. 结果
  16. [张三]
  17. [张三, 李四]
thing.arrayList = (ArrayList<String>)this.arrayList.clone();

该方法就实现了完全的拷贝,两个对象之间没有任何关系,各自进行修改,互不影响,这就是深拷贝。深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝。

注意:深拷贝和浅拷贝不建议同时使用。

(3)clone与final

对象的clone与对象内的final关键字是有冲突的。final类型只能赋一次值,不能重复赋值,因此无法实现深拷贝。

注意:要使用clone方法,类的成员变量上不要增加final关键字。


原型模式实例

银行多线程发送大量广告邮件,当然邮件中一般都含有客户信息,需要从数据库中调。为防止大量发送邮件被邮件服务器误认为是垃圾邮件,我们需要在邮件头增加一些伪造数据。

我们根据需求和原型模型的类图设计如下类图:

类图中:

  • AdvTemplate类:广告信的模板,一般都是从数据库取出,生成一个BO或者是DTO,这里使用一个静态的值来作代表;
  • Cloneable接口:Java自带的一个接口。
  • Mail类:邮件类,发送的就是这个类。Mail实现了Cloneable接口,在Mail类中覆写clone()方法。

  
  1. public class AdvTemplate {
  2. //广告信名称
  3. private String advSubject = "XX银行国庆信用卡抽奖活动";
  4. //广告信内容
  5. private String advContext = "国庆抽奖活动通知:只有刷卡就送一百万!...";
  6. //取得广告信的名称
  7. public String getAdvSubject() {
  8. return this.advSubject;
  9. }
  10. //取得广告信的内容
  11. public String getAdvContext() {
  12. return this.advContext;
  13. }
  14. }

  
  1. public class Mail implements Cloneable{
  2. //收件人
  3. private String receiver;
  4. //邮件名称
  5. private String subject;
  6. //称谓
  7. private String appellation;
  8. //邮件内容
  9. private String contxt;
  10. //邮件尾部,一般加上“XXX版权所有”等信息
  11. private String tail;
  12. public Mail(AdvTemplate advTemplate) {
  13. this.contxt = advTemplate.getAdvContext();
  14. this.subject = advTemplate.getAdvSubject();
  15. }
  16. @Override
  17. public Mail clone() {
  18. Mail mail = null;
  19. try {
  20. mail = (Mail) super.clone();
  21. } catch (CloneNotSupportedException e) {
  22. e.printStackTrace();
  23. }
  24. return mail;
  25. }
  26. public String getReceiver() {
  27. return receiver;
  28. }
  29. public void setReceiver(String receiver) {
  30. this.receiver = receiver;
  31. }
  32. public String getSubject() {
  33. return subject;
  34. }
  35. public void setSubject(String subject) {
  36. this.subject = subject;
  37. }
  38. public String getAppellation() {
  39. return appellation;
  40. }
  41. public void setAppellation(String appellation) {
  42. this.appellation = appellation;
  43. }
  44. public String getContxt() {
  45. return contxt;
  46. }
  47. public void setContxt(String contxt) {
  48. this.contxt = contxt;
  49. }
  50. public String getTail() {
  51. return tail;
  52. }
  53. public void setTail(String tail) {
  54. this.tail = tail;
  55. }
  56. }

  
  1. import java.util.Random;
  2. import com.sfq.action.AdvTemplate;
  3. import com.sfq.action.Mail;
  4. public class Client {
  5. //发送账单的数量,这个值从数据库中取得
  6. private static int MAX_COUNT = 6;
  7. public static void main(String[] args) {
  8. // 模拟发送邮件
  9. int i = 0;
  10. //把模板定义出来,这个是从数据库中获得
  11. Mail mail = new Mail( new AdvTemplate());
  12. mail.setTail( "XX银行版权所有");
  13. while (i < MAX_COUNT) {
  14. //以下是每封邮件不同的地方
  15. Mail cloneMail = mail.clone();
  16. cloneMail.setAppellation(getRandString( 5)+ " 先生(女士)");
  17. cloneMail.setReceiver(getRandString( 5) + "@"+ getRandString( 8) + ".com");
  18. //然后发送邮件
  19. sendMail(cloneMail);
  20. i ++;
  21. }
  22. }
  23. //发送邮件
  24. private static void sendMail(Mail mail) {
  25. System.out.println( "标题:"+mail.getSubject()+ "\t收件人:"+mail.getReceiver()+ "\t...发送成功!");
  26. }
  27. //获得指定长度的随机字符串
  28. private static String getRandString(int maxLength) {
  29. String source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  30. StringBuffer sb = new StringBuffer();
  31. Random rand = new Random();
  32. for( int i = 0;i < maxLength;i++) {
  33. sb.append(source.charAt(rand.nextInt(source.length())));
  34. }
  35. return sb.toString();
  36. }
  37. }
  38. 结果
  39. 标题:XX银行国庆信用卡抽奖活动 收件人:uObQB @dJUNZzxo.com ...发送成功!
  40. 标题:XX银行国庆信用卡抽奖活动 收件人:dxMNq @zkWYDNwm.com ...发送成功!
  41. 标题:XX银行国庆信用卡抽奖活动 收件人:FJXVs @irEoxeLY.com ...发送成功!
  42. 标题:XX银行国庆信用卡抽奖活动 收件人:mADRn @WOpmmnFp.com ...发送成功!
  43. 标题:XX银行国庆信用卡抽奖活动 收件人:ovZmn @fTNpoddl.com ...发送成功!
  44. 标题:XX银行国庆信用卡抽奖活动 收件人:cVbzh @CPAVzlho.com ...发送成功!

 


转载:https://blog.csdn.net/u012839256/article/details/105934051
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场