应用前景:今天,我们将学习23种设计模式中的第五种原型模式。它主要用于创建重复对象,并确保系统的性能。让我们来看看这种设计模式的巧妙之处。
在我们的应用程序中,有些对象的结构可能很复杂,但我们需要经常使用它们。如果我们在这个时候不断地创建这个对象,我们必然会损失系统内存。此时,我们需要使用原型模式来克隆结构复杂且需要频繁使用的对象。因此,原型模式是用原型实例指定创建对象的类型,并通过复制这些原型创建新对象。
原型模式的概念:以创建的实例为原型,复制原型对象,创建与原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的类型。这样创建对象是非常有效的,没有必要知道对象创建的细节。
原型模式的特点:该模型的主要优点如下:
- 性能提高。
- 避免构造函数的约束。
主要缺点如下:
- 配备克隆方法需要综合考虑类别的功能,这对于新类别来说并不难,但对于现有类别来说并不一定容易,特别是当一个类别引用不支持串行的间接对象时,或引用包含循环结构。
- 必须实现
Cloneable
接口。
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类别:实现抽象原型类别
clone()
该方法是可复制的对象。 - 访问类别:在特定的原型类别中使用
clone()
复制新对象的方法。
1.克隆羊问题的传统方式
总结:
- 优点是易于理解,操作简单方便。
- 在创建新的对象时,总是需要重新获得原始对象的属性,如果创建的对象更复杂,效率更低。
- 总是需要重新初始化对象,而不是动态获得对象运行时的状态,这是不够灵活的。
2.克隆羊问题在原型模式下:
浅拷贝:
介绍浅拷贝:
- 对于数据类型是基本数据类型的成员变量,浅拷贝将直接传递值,即将属性值复制给新对象。
- 对于数据类型是参考数据类型的成员变量,如成员变量是一个数组,一个类别的对象,然后浅复制将被引用和传输,即只复制成员变量的参考值(内存地址)到一个新的对象。因为事实上,两个对象的成员变量都指向同一个例子。在这种情况下,在一个对象中修改成员变量会影响另一个对象中成员变量的值。
- 下面我们说的克隆羊其实是浅拷贝,浅拷贝是默认的
clone()
实现方法,sheep = (Sheep) super.clone();
。
克隆羊原型(Sheep)
package cn.ppdxzz.prototype.shallowclone;/** * Description:克隆羊原型 */public class Sheep implements Cloneable { private String name;//姓名 private int age;//年龄 private String color;//颜色 private String type = "蒙古羊";//种类 public Sheep(String name, int age, String color) { super(); this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + ", type='" + type + '\'' + '}'; } //克隆这个例子,使用默认的clone方法来完成 @Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return sheep; }}
克隆羊的原型模式
package cn.ppdxzz.prototype.shallowclone;/** * Description:克隆羊的原型模式 */public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("多利",1,"白色"); Sheep sheep1 = (Sheep)sheep.clone();//克隆 Sheep sheep2 = (Sheep)sheep.clone();//克隆 Sheep sheep3 = (Sheep)sheep.clone();//克隆 System.out.println("sheep1 = "+ sheeep1); System.out.println("sheep2 = "+ sheeep2); System.out.println("sheep3 = "+ sheep3); }}
深拷贝:
深拷贝介绍:
- 复制对象所有基本数据类型的成员变量值。
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量引用的对象,直到对象可以到达所有对象。也就是说,对象应该复制整个对象。
实现深拷贝的方法:
方法一:重写clone方法,实现深度复制。
方法二:深拷贝(推荐使用这种深拷贝)是通过对象序列化实现的。
需要深度复制的对象
package cn.ppdxzz.prototype.deepclone;import java.io.Serializable;/** * Description:要深度复制一个对象 */public class DeepCloneClass implements Cloneable, Serializable { private String cloneName;/////深拷贝对象的名称 private String cloneDescription;////深拷贝对象的描述 public DeepCloneClass() { } public DeepCloneClass(String cloneName, String cloneDescription) { this.cloneName = cloneName; this.cloneDescription = cloneDescription; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}
深度复制原型对象(包括引用数据类型)
package cn.ppdxzz.prototype.deepclone;import java.io.*;/** * Description:深度复制原型对象(包括引用数据类型) */public class DeepPrototype implements Cloneable, Serializable { private String name;///String类型 private DeepCloneClass deepCloneClass;//引用数据类型,对象进行深度复制 public String getName() { return name; } public void setName(String name) { this.name = name; } public DeepCloneClass getDeepCloneClass() { return deepCloneClass; } public void setDeepCloneClass(DeepCloneClass deepCloneClass) { this.deepCloneClass = deepCloneClass; } ///方法一实现深拷贝 @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; //1.在这里复制基本数据类型和String类型 deep = super.clone(); //2.单独处理引用数据类型的数据 DeepPrototype deepPrototype = (DeepPrototype) deep; deepPrototype.setDeepCloneClass((DeepCloneClass)deepCloneClass.clone()); return deepPrototype; } //方法二实现深拷贝 public Object deepClone() { //1.创建流对象 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { //2.序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); //3.反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); DeepPrototype copyObj = (DeepPrototype) ois.readObject(); return copyObj; } catch (Exception e) { e.printStackTrace(); return null; } finally { //4.关闭流量 if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }}
客户端验证是否可以进行深度复制
package cn.ppdxzz.prototype.deepclone;/** * Description:客户端 */public class Client { public static void main(String[] args) throws Exception { DeepPrototype d = new DeepPrototype(); d.setName("深拷贝"); d.setDeepCloneClass(new DeepCloneClass("深拷贝对象","我需要深度复制的对象")); //1.方法一完成深拷贝 DeepPrototype dClone = (DeepPrototype) d.clone(); System.out.println("原型.name = " + d.getName() + " 原型.deepCloneClass = " + d.getDeepCloneClass().hashCode()); System.out.println("克隆.name = " + dClone.getName() + " 克隆.deepCloneClass = " + dClone.getDeepCloneClass().hashCode()); //2.方法2完成深拷贝 DeepPrototype dClone = (DeepPrototype)d.deepClone(); System.out.println("原型.name = " + d.getName() + " 原型.deepCloneClass = " + d.getDeepCloneClass().hashCode()); System.out.println("克隆.name = " + dClone.getName() + " 克隆.deepCloneClass = " + dClone.getDeepCloneClass().hashCode()); }}
原型模式源码分析:Spring中原型bean的创建是使用的原型模式 。
- 当创建新对象更加复杂时,可以使用原型模式来简化对象的创建过程,同时也可以提高效率。
- 动态获取对象运行时的状态,而不是重新初始化对象。
- 如果原始对象发生变化(增加或减少属性),其他克隆对象也会发生相应的变化,无需修改代码。
- 可能需要更复杂的代码来实现深克隆。
- 注:它需要为每个类配备一种克隆方法,这对新类来说并不难,但在改造现有类时,需要修改其源代码,这是违反的
OCP
原则。
到目前为止,已经完成了创造性的五种设计模式,即单例模式、工厂方法模式、抽象工厂模式、建筑商模式和原型模式。下一阶段,我们将开始学习结构设计模式。请期待它。
