image-20200517144621628

在聊原型模式之前,我们来思考一个小问题,传统的方式我们是如何克隆对象呢?

那我们以多利羊(Sheep)为例,来说明上述这个问题,具体代码见下面:

多利羊(Sheep)

  1. public class Sheep {
  2. private String sname;
  3. private Date birthday;
  4. public Sheep(String sname, Date birthday) {
  5. this.sname = sname;
  6. this.birthday = birthday;
  7. }
  8. public String getSname() {
  9. return sname;
  10. }
  11. public void setSname(String sname) {
  12. this.sname = sname;
  13. }
  14. public Date getBirthday() {
  15. return birthday;
  16. }
  17. public void setBirthday(Date birthday) {
  18. this.birthday = birthday;
  19. }
  20. }

测试类(Client)

  1. public class Client {
  2. public static void main(String[] args) {
  3. Sheep sheep=new Sheep("多利",new Date());
  4. Sheep sheep2=new Sheep(sheep.getSname(),sheep.getBirthday());
  5. System.out.println("sheep:"+sheep.getSname()+sheep.getBirthday());
  6. System.out.println("sheep2:"+sheep2.getSname()+sheep2.getBirthday());
  7. }
  8. }

那我们来分析一下:

1、在创建新对象时,总是需要获取原始对象的属性,如果我们在实际项目中创建的对象复杂的话,效率是很低的。

2、总是需要重新初始化对象,而不是动态地获取对象运行时的状态,不够灵活。

有什么方法可以改进吗?,原型模式闪亮登场

大家都知道在Java中有一个Object类,这个类提供了一个clone()方法,该方法可以将Java对象复制一份,前提是该类实现一个Cloneable接口,这就是原型模式的源头。

什么是原型模式:

用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。

看了这个定义可能有的小伙伴们比较蒙,那我们来撸一把代码来说明(多利羊克隆):

多利羊(Sheep)实现Cloneable

  1. public class Sheep implements Cloneable {
  2. private String sname;
  3. private Date birthday;
  4. private Sheep mother;
  5. public Sheep getMother() {
  6. return mother;
  7. }
  8. public void setMother(Sheep mother) {
  9. this.mother = mother;
  10. }
  11. public Sheep(String sname, Date birthday) {
  12. this.sname = sname;
  13. this.birthday = birthday;
  14. }
  15. public String getSname() {
  16. return sname;
  17. }
  18. public void setSname(String sname) {
  19. this.sname = sname;
  20. }
  21. public Date getBirthday() {
  22. return birthday;
  23. }
  24. public void setBirthday(Date birthday) {
  25. this.birthday = birthday;
  26. }
  27. @Override
  28. public String toString() {
  29. return "Sheep{" +
  30. "sname='" + sname + '\'' +
  31. ", birthday=" + birthday +
  32. '}';
  33. }
  34. @Override
  35. protected Object clone() throws CloneNotSupportedException {
  36. Sheep sheep=null;
  37. try {
  38. sheep=(Sheep) super.clone();
  39. }catch (Exception e){
  40. e.printStackTrace();
  41. }
  42. return sheep;
  43. }
  44. }

克隆方法的测试(Client)

  1. public class Client {
  2. public static void main(String[] args) throws CloneNotSupportedException {
  3. Sheep sheep=new Sheep("多利",new Date());
  4. Sheep mother=new Sheep("多利的妈妈",new Date());
  5. sheep.setMother(mother);
  6. Sheep sheep1=(Sheep) sheep.clone();
  7. System.out.println("sheep:"+sheep);
  8. System.out.println("sheep1:"+sheep1);
  9. System.out.println("sheep:"+sheep.getMother().hashCode());
  10. System.out.println("sheep1:"+sheep1.getMother().hashCode());
  11. }
  12. }

运行结果:hashcode 的值一样

sheep:Sheep{sname=’多利’, birthday=Sun May 17 11:56:58 CST 2020}
sheep1:Sheep{sname=’多利’, birthday=Sun May 17 11:56:58 CST 2020}
sheephashcode:2061475679
sheep1hashcode:2061475679

浅克隆的介绍:

对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

对于数据类型是引用数据类型的成员变量(数组,对象),浅拷贝会进行引用传递,也就是将成员变量的引用值(内存地址)复制一份给新的对象,因为实际上两个对象的该成员变量都指向同一个实例(前面克隆羊就是浅拷贝,使用clone()方法实现)。

再次说明:其实就是克隆多利羊之后,克隆的对象没有被真正的复制一份,而是引用指向第一个对象的属性空间。

画图说明:

image-20200517121722748

具体代码如下见上面(多利羊克隆)

深克隆的介绍:

1、对对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。

image-20200517124535384

深克隆实现的方式:

1、重写clone()方法实现深克隆

2、通过对象序列化实现深克隆

第一种方式:重写clone()方法实现深克隆,参考下面代码实现:

  1. public class Sheep implements Cloneable {
  2. private String sname;
  3. private Date birthday;
  4. public Mother mother;
  5. public Sheep() {
  6. }
  7. public Sheep(String sname, Date birthday,Mother mother ) {
  8. this.sname = sname;
  9. this.birthday = birthday;
  10. this.mother=mother;
  11. }
  12. @Override
  13. public String toString() {
  14. return "Sheep{" +
  15. "sname='" + sname + '\'' +
  16. ", birthday=" + birthday +
  17. ", mother=" + mother +
  18. '}';
  19. }
  20. @Override
  21. protected Object clone() throws CloneNotSupportedException {
  22. Sheep sheep=null;
  23. try {
  24. Object obj= super.clone();
  25. sheep=(Sheep) obj;
  26. sheep.mother=(Mother) mother.clone();
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. return sheep;
  31. }
  32. }

多利的妈咪

  1. public class Mother implements Cloneable {
  2. private int age;
  3. private String name;
  4. public Mother(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. @Override
  9. protected Object clone() throws CloneNotSupportedException {
  10. return super.clone();
  11. }
  12. }

客户端测试:

  1. public class Client {
  2. public static void main(String[] args) throws CloneNotSupportedException {
  3. Sheep sheep=new Sheep("多利",new Date(),new Mother(12,"多利的妈妈"));
  4. Sheep sheep1=(Sheep) sheep.clone();
  5. System.out.println("sheep:"+sheep);
  6. System.out.println("sheep1:"+sheep1);
  7. System.out.println("sheephashcode:"+sheep.mother.hashCode());
  8. System.out.println("sheep1hashcode:"+sheep1.mother.hashCode());
  9. }
  10. }

运行结果:hashcode 的值不一样

sheep:Sheep{sname=’多利’, birthday=Sun May 17 13:20:51 CST 2020, mother=com.designpattern.pattern.prototypepattern.sprototype.Mother@7adf9f5f}
sheep1:Sheep{sname=’多利’, birthday=Sun May 17 13:20:51 CST 2020, mother=com.designpattern.pattern.prototypepattern.sprototype.Mother@85ede7b}
sheephashcode:2061475679
sheep1hashcode:140435067

第二种方式:序列化和反序列化

需要在Sheep ,Mother 类实现一个序列化 Serializable,具体代码和上面第一种方式的代码一样,就是这Sheep代码中增加以下这段核心代码:

  1. // 通过对象的序列化实现
  2. public Object deepClone() throws Exception {
  3. ByteArrayOutputStream bos=new ByteArrayOutputStream();
  4. ObjectOutputStream oos=new ObjectOutputStream(bos);
  5. oos.writeObject(this);
  6. ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
  7. Sheep sheep=(Sheep) ois.readObject();
  8. return sheep;
  9. }

运行结果:

sheep:Sheep{sname=’多利’, birthday=Sun May 17 13:51:23 CST 2020, mother=com.designpattern.pattern.prototypepattern.sprototype.Mother@72ea2f77}
sheep1:Sheep{sname=’多利’, birthday=Sun May 17 13:51:23 CST 2020, mother=com.designpattern.pattern.prototypepattern.sprototype.Mother@17f052a3}
sheephashcode:1927950199
sheep1hashcode:401625763

Spring 中bean 的scope 属性的声明:

  1. <bean id="student" class="com.Student" scope="prototype"/>
  1. public class ProtoType {
  2. public static void main(String[] args) {
  3. // TODO Auto-generated method stub
  4. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  5. // 获取monster[通过id获取monster]
  6. Object bean = applicationContext.getBean("student");
  7. System.out.println("bean:" + bean);
  8. Object bean2 = applicationContext.getBean("student");
  9. System.out.println("bean1" + bean2);
  10. System.out.println(bean == bean2);
  11. }
  12. }

运行结果

bean:com.atguigu.spring.bean.Student@52bf72b5
bean1com.atguigu.spring.bean.Student@37afeb11
false

优点:

1、创建新的对象比较复杂时,可以利用原型模式创建新的对象,不用重新初始化对象,而是动态地获得对象运行时的状态。

2、如果原始对象发生变化,其克隆的对象也会发生响应的变化,不需要修改代码来实现

缺点:

需要为每一个类配置一个克隆方法,需要器修改源代码,不符合OCP原则。

技术之路还很长,慢慢来吧,时间会证明一切。

版权声明:本文为xiaofuzi123456原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiaofuzi123456/p/12905349.html