GOF23设计模式之原型模式(prototype)
一、原型模式概述
1.通过new产生一个对象需要非常繁琐的数据准备和访问权限,则可以使用原型模式。
2.就是java中的克隆技术,以某个对象为原型,复制出新的对象,显然,新的对象具备原型对象的特点。
3.优势:效率高(直接克隆,避免了重新执行构造函数的过程)。
4.克隆类似于new,但是不同于new。
new创建出来的对象属性采用默认值,克隆出来的对象属性值完全和原型对象相同,并且克隆出的新对象不会影响原型对象,然后,再修改克隆对象的值。
是否使用原型模式比较:
1.如果需要创建大量不费时的对象,new的对象和clone的对象效率相当
2.如果需要创建大量耗时的对象,建议使用原型模式
二、原型模式三种实现方式
1.浅复制:类中实现 Cloneable接口,重写clone方法
2.深复制:类中实现 Cloneable接口,重写clone方法时将对象一起克隆
3.深复制:类中实现 Serializable接口,利用反序列化实现深克隆
三、使用浅复制实现原型模式
1.使用原型模式克隆多利羊
1 /** 2 * 原型模式(浅克隆) 3 * @author CL 4 * 5 */ 6 public class Sheep implements Cloneable { 7 private String name; 8 9 public Object clone() throws CloneNotSupportedException { 10 return super.clone(); 11 } 12 13 public Sheep() { 14 } 15 16 public Sheep(String name) { 17 this.name = name; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 }
2.测试
1 public class TestSheep { 2 3 public static void main(String[] args) throws Exception { 4 Sheep s1 = new Sheep("多利"); 5 Sheep s2 = (Sheep) s1.clone(); //克隆对象 6 7 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()); 8 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()); 9 10 s2.setName("少利"); //对克隆对象修改,不影响原对象的属性值 11 12 System.out.println("\n--------修改克隆对象的属性值后---------\n"); 13 14 15 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()); 16 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()); 17 } 18 19 }
控制台输出:
366712642(原型对象)-->多利 1829164700(克隆对象)-->多利 --------修改克隆对象的属性值后--------- 366712642(原型对象)-->多利 1829164700(克隆对象)-->少利
四、使用深复制实现原型模式(1)
1.使用原型模式克隆多利羊
1 import java.util.Date; 2 3 /** 4 * 原型模式(深克隆) 5 * @author CL 6 * 7 */ 8 public class Sheep implements Cloneable { 9 private String name; 10 11 private Birthday birthday; 12 13 public Object clone() throws CloneNotSupportedException { 14 Sheep s = (Sheep) super.clone(); //克隆对象 15 s.birthday = (Birthday) this.birthday.clone(); 16 return s; 17 } 18 19 public Sheep() { 20 } 21 22 public Sheep(String name, Birthday birthday) { 23 this.name = name; 24 this.birthday = birthday; 25 } 26 27 public String getName() { 28 return name; 29 } 30 31 public void setName(String name) { 32 this.name = name; 33 } 34 35 public Birthday getBirthday() { 36 return birthday; 37 } 38 39 public void setBirthday(Birthday birthday) { 40 this.birthday = birthday; 41 } 42 43 44 } 45 46 class Birthday implements Cloneable { 47 private Date birthday; 48 49 protected Object clone() throws CloneNotSupportedException { 50 return super.clone(); 51 } 52 53 public Birthday() { 54 } 55 56 public Birthday(Date birthday) { 57 this.birthday = birthday; 58 } 59 60 public Date getBirthday() { 61 return birthday; 62 } 63 64 public void setBirthday(Date birthday) { 65 this.birthday = birthday; 66 } 67 }
2.测试
1 import java.util.Date; 2 3 /** 4 * 测试原型模式(深克隆) 5 * @author CL 6 * 7 */ 8 public class TestSheep { 9 10 public static void main(String[] args) throws Exception { 11 Birthday date = new Birthday(new Date(5456464L)); 12 Sheep s1 = new Sheep("多利", date); 13 Sheep s2 = (Sheep) s1.clone(); //克隆对象 14 15 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()+"-->"+s1.getBirthday().getBirthday()); 16 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()+"-->"+s2.getBirthday().getBirthday()); 17 18 date.setBirthday(new Date()); //对原对象修改,克隆对象的属性值不改变 19 20 System.out.println("\n--------修改克隆对象的属性值后---------\n"); 21 22 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()+"-->"+s1.getBirthday().getBirthday()); 23 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()+"-->"+s2.getBirthday().getBirthday()); 24 } 25 26 }
控制台输出:
366712642(原型对象)-->多利-->Thu Jan 01 09:30:56 CST 1970 1550089733(克隆对象)-->多利-->Thu Jan 01 09:30:56 CST 1970 366712642(原型对象)-->多利-->Fri Dec 29 17:03:26 CST 2017 1550089733(克隆对象)-->多利-->Thu Jan 01 09:30:56 CST 1970
五、使用深复制实现原型模式(2)
1.使用原型模式克隆多利羊
1 import java.io.Serializable; 2 import java.util.Date; 3 4 /** 5 * 原型模式(利用反序列化实现深克隆) 6 * @author CL 7 * 8 */ 9 public class Sheep implements Serializable { 10 private String name; 11 12 private Birthday birthday; 13 14 public Sheep() { 15 } 16 17 public Sheep(String name, Birthday birthday) { 18 this.name = name; 19 this.birthday = birthday; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public Birthday getBirthday() { 31 return birthday; 32 } 33 34 public void setBirthday(Birthday birthday) { 35 this.birthday = birthday; 36 } 37 38 } 39 40 class Birthday implements Serializable { 41 private Date birthday; 42 43 public Birthday() { 44 } 45 46 public Birthday(Date birthday) { 47 this.birthday = birthday; 48 } 49 50 public Date getBirthday() { 51 return birthday; 52 } 53 54 public void setBirthday(Date birthday) { 55 this.birthday = birthday; 56 } 57 }
2.测试
1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 import java.io.ObjectInputStream; 4 import java.io.ObjectOutputStream; 5 import java.util.Date; 6 7 /** 8 * 测试原型模式(利用反序列化实现深克隆) 9 * @author CL 10 * 11 */ 12 public class TestSheep { 13 14 public static void main(String[] args) throws Exception { 15 Birthday date = new Birthday(new Date(5456464L)); 16 Sheep s1 = new Sheep("多利", date); 17 18 //利用反序列化实现深克隆 19 //1. 序列化 20 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 21 ObjectOutputStream oos = new ObjectOutputStream(bos); 22 oos.writeObject(s1); 23 byte[] bytes = bos.toByteArray(); 24 25 //2. 反序列化 26 ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 27 ObjectInputStream ois = new ObjectInputStream(bis); 28 Sheep s2 = (Sheep) ois.readObject(); 29 30 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()+"-->"+s1.getBirthday().getBirthday()); 31 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()+"-->"+s2.getBirthday().getBirthday()); 32 33 date.setBirthday(new Date()); //对原对象修改,克隆对象的属性值不改变 34 35 System.out.println("\n--------------------修改克隆对象的属性值后---------------------\n"); 36 37 System.out.println(s1.hashCode()+"(原型对象)-->"+s1.getName()+"-->"+s1.getBirthday().getBirthday()); 38 System.out.println(s2.hashCode()+"(克隆对象)-->"+s2.getName()+"-->"+s2.getBirthday().getBirthday()); 39 } 40 41 }
控制台输出:
1442407170(原型对象)-->多利-->Thu Jan 01 09:30:56 CST 1970 1173230247(克隆对象)-->多利-->Thu Jan 01 09:30:56 CST 1970 --------------------修改克隆对象的属性值后--------------------- 1442407170(原型对象)-->多利-->Fri Dec 29 17:11:21 CST 2017 1173230247(克隆对象)-->多利-->Thu Jan 01 09:30:56 CST 1970
六、原型模式常见应用场景
1.原型模式很少单独出现,一般是和工厂方法模式一起出现。通过clone的方法创建一个对象,然后由工厂方法提供给调用者;
2.Spring中的bean的创建其实就是两种:单例模式和原型模式(+工厂模式);
3.………………
//对原型对象的修改