有些对象创建过程较为复杂,而且有些时候需要频繁的创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后复制这个原型对象的方法创建更多同类型的对象。这就是原型模式的动机。
原型模式的主要思想是基于现有对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本。在以下几个场景中,使用原型模式会更简单效率也更高。
(1)当一个系统应该独立于它的产品创建、构成和表示时,要使用原型模式。
(2)当要实例化的类是在运行时刻指定时,例如:动态装载。
(3)为了避免创建一个与产品类层次平行的类层次时。
(4)当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一些(也就是当我们在处理一些比较简单的对象,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,使用原型模式更合适)。
原型模式主要用于对象的复制,它的核心是原型类Prototype。Prototype类需要具备以下两个条件:
(1)实现Cloneable接口: JAVA语言中有一个Cloneable接口,他的作用只用一个,就是在运行时通知虚拟机可以安全的在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被复制,否则会抛出异常CloneNotSupportedExceotion
(2)重写Object类中的clone方法。clone()作用是返回对象的一个复制,但其作用域是protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法作用域改为public。
原型模式实现广告模板克隆:
广告模板:
1 public class Adv { 2 private String subject; 3 private String contxt; 4 5 public String getSubject() { 6 return subject; 7 } 8 public void setSubject(String subject) { 9 this.subject = subject; 10 } 11 public String getContxt() { 12 return contxt; 13 } 14 public void setContxt(String contxt) { 15 this.contxt = contxt; 16 } 17 18 }
mail类 实现可对自身进行克隆:
1 /* 2 * 实现Cloneable 接口 可以对自身进行克隆 3 */ 4 public class Mail implements Cloneable{ 5 private String recrviver = "AAA"; 6 private String subject = "BBB"; 7 private String appellation = "CCC"; 8 private String tail = "DDD"; 9 private String contxt = "EEE"; 10 11 public Mail() { 12 super(); 13 } 14 public Mail(Adv adv) { 15 contxt = adv.getContxt(); 16 subject = adv.getSubject(); 17 } 18 @Override 19 public Mail clone() { 20 Mail mail = null; 21 try {
//为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象
// 然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中 22 mail= (Mail) super.clone(); 23 } catch (CloneNotSupportedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 return mail; 28 } 29 30 @Override 31 public String toString() { 32 return "Mail [recrviver=" + recrviver + ", subject=" + subject + ", appellation=" + appellation + ", tail=" + tail 33 + ", contxt=" + contxt + "]"; 34 } 35 public String getRecrviver() { 36 return recrviver; 37 } 38 public void setRecrviver(String recrviver) { 39 this.recrviver = recrviver; 40 } 41 public String getSubject() { 42 return subject; 43 } 44 public void setSubject(String subject) { 45 this.subject = subject; 46 } 47 public String getAppellation() { 48 return appellation; 49 } 50 public void setAppellation(String appellation) { 51 this.appellation = appellation; 52 } 53 public String getTail() { 54 return tail; 55 } 56 public void setTail(String tail) { 57 this.tail = tail; 58 } 59 public String getContxt() { 60 return contxt; 61 } 62 public void setContxt(String contxt) { 63 this.contxt = contxt; 64 } 65 66 }
测试类:
1 public class Test { 2 3 public static void main(String[] args) { 4 // Mail m1 = new Mail(); 5 // Mail m2 = m1.clone(); 6 // System.out.println( m2.toString()); 7 8 9 Mail mail= new Mail(new Adv());//Adv是广告的模板 10 for(int i = 0; i<5;i++){ 11 Mail m = mail.clone();//克隆原型邮件 12 m.setRecrviver(""+i);//向邮件中添加不同的收件信息 13 System.out.println(m.toString()); 14 15 } 16 17 } 18 19 }
1 Mail [recrviver=0, subject=null, appellation=CCC, tail=DDD, contxt=null] 2 Mail [recrviver=1, subject=null, appellation=CCC, tail=DDD, contxt=null] 3 Mail [recrviver=2, subject=null, appellation=CCC, tail=DDD, contxt=null] 4 Mail [recrviver=3, subject=null, appellation=CCC, tail=DDD, contxt=null] 5 Mail [recrviver=4, subject=null, appellation=CCC, tail=DDD, contxt=null]
⑴浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象内存储的引用,而不复制它所引用的对象。
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
从以下我们可以看出,clone所进行的时浅克隆们m2只复制了m1所存储的引用当我们对m2做出更改时m1也会改变:
1 public static void main(String[] args) { 2 3 Mail m1 = new Mail(); 4 Mail m2 = m1.clone(); 5 System.out.println( m2.toString()); 6 7 System.out.println(m1.equals(m2)); 8 System.out.println(m1.getAppellation().equals(m2.getAppellation())); 9 10 // Mail mail= new Mail(new Adv());//Adv是广告的模板 11 // for(int i = 0; i<5;i++){ 12 // Mail m = mail.clone();//克隆原型邮件 13 // m.setRecrviver(""+i);//向邮件中添加不同的收件信息 14 // System.out.println(m.toString()); 15 // 16 // } 17 18 } 19 20 }
1 Mail [recrviver=AAA, subject=BBB, appellation=CCC, tail=DDD, contxt=EEE] 2 false 3 true