23种设计模式之原型模式
原型模式
1、基本介绍
1)原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象。
2)原型模式是一种创建型设计模式,允许一个对象再创建另一个可定制的对象,而无需知道如何创建的细节。
3)原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()。
4)结论:使用原型模式使程序有更高的效率和扩展性。
5)类图:
- Prototype:原型类,声明一个克隆自己的接口。
- ConcretePrototype:具体的原型类,实现一个克隆自己的操作。
- Client:让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
6)代码:
// 原型类 public class Sheep implements Cloneable { private String name; private Integer age; private String color; public Sheep() { } public Sheep(String name, Integer age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer 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 + '\'' + '}'; } /** * 克隆该实例,使用默认的clone方法来完成 * @param [] * @date 2019/7/30 15:40 * @return java.lang.Object **/ @Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return sheep; } } // Client public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("Tom", 1, "白色"); // 使用克隆 Sheep sheep2 = (Sheep) sheep.clone(); Sheep sheep3 = (Sheep) sheep.clone(); Sheep sheep4 = (Sheep) sheep.clone(); Sheep sheep5 = (Sheep) sheep.clone(); System.out.println(sheep); System.out.println(sheep2); System.out.println(sheep3); System.out.println(sheep4); System.out.println(sheep5); } } /* 结果: Sheep{name='Tom', age=1, color='白色'} Sheep{name='Tom', age=1, color='白色'} Sheep{name='Tom', age=1, color='白色'} Sheep{name='Tom', age=1, color='白色'} Sheep{name='Tom', age=1, color='白色'} */
2、浅拷贝和深拷贝
2.1、浅拷贝的介绍
1)对于数据类型是基本数据类型(包含String)的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)对于数据类型是引用数据类型的成员变量,比如是数组、某个类的对象,那么浅拷贝会进行引用传递,实际就是两个对象的该成员变量指向同一个实例。在这种情况下,在一个对象修改该成员变量时,会影响到另一个对象的该成员变量值。
3)代码中的克隆羊例子就是浅拷贝。
4)浅拷贝是使用默认的clone()方法来实现。
2.2、深拷贝的介绍
1)复制对象的所有基本数据类型(包含String)的成员变量值。
2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
3)实现1:重写clone方法
4)实现2:通过对象序列化(推荐,实现看代码)
2.3、代码
// DeepCloneableTarget public class DeepCloneableTarget implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private String cloneName; private String cloneClass; public DeepCloneableTarget(String cloneName, String cloneClass) { this.cloneName = cloneName; this.cloneClass = cloneClass; } /** * 该类中成员变量都是基本属性类型,所以使用默认clone方法 * */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "DeepCloneableTarget{" + "cloneName='" + cloneName + '\'' + ", cloneClass='" + cloneClass + '\'' + '}'; } } // DeepPrototype public class DeepPrototype implements Serializable, Cloneable { /** String数据类型 */ private String name; /** 引用数据类型 */ private DeepCloneableTarget target; public DeepPrototype() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public DeepCloneableTarget getTarget() { return target; } public void setTarget(DeepCloneableTarget target) { this.target = target; } /** * 深拷贝方式一:使用clone方法 * */ @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; // 完成基本数据类型(属性)和String类型的克隆 deep = super.clone(); // 对引用类型属性进行单独处理 DeepPrototype deepPrototype = (DeepPrototype) deep; deepPrototype.target = (DeepCloneableTarget) target.clone(); return deepPrototype; } /** * 深拷贝方式二:通过对象的序列化(推荐) * */ public Object deepClone() { // 创建流对象 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; // 创建该类对象 DeepPrototype clone = null; try { // 序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 反序列 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); clone = (DeepPrototype) ois.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 try { if (bos != null) { bos.close(); } if (oos != null) { oos.close(); } if (bis != null) { bis.close(); } if (ois != null) { ois.close(); } } catch (Exception e) { e.printStackTrace(); } } return clone; } @Override public String toString() { return "DeepPrototype{" + "name='" + name + '\'' + ", target=" + target + '}'; } } // Client public class Client { public static void main(String[] args) throws CloneNotSupportedException { DeepPrototype deepPrototype = new DeepPrototype(); deepPrototype.setName("tom"); deepPrototype.setTarget(new DeepCloneableTarget("cloneName", "cloneClass")); // 通过clone方法实现深拷贝 DeepPrototype deepPrototype1 = (DeepPrototype) deepPrototype.clone(); System.out.println("通过clone方法实现深拷贝"); System.out.println(deepPrototype + "" + deepPrototype.getTarget().hashCode()); System.out.println(deepPrototype1 + "" + deepPrototype1.getTarget().hashCode()); System.out.println("------------------------------"); // 通过序列化实现深拷贝 DeepPrototype deepPrototype2 = (DeepPrototype) deepPrototype.deepClone(); System.out.println("通过序列化实现深拷贝"); System.out.println(deepPrototype + "" + deepPrototype.getTarget().hashCode()); System.out.println(deepPrototype2 + "" + deepPrototype2.getTarget().hashCode()); } } /* // 结果: 通过clone方法实现深拷贝 DeepPrototype{name='tom', target=DeepCloneableTarget{cloneName='cloneName', cloneClass='cloneClass'}}460141958 DeepPrototype{name='tom', target=DeepCloneableTarget{cloneName='cloneName', cloneClass='cloneClass'}}1163157884 ------------------------------ 通过序列化实现深拷贝 DeepPrototype{name='tom', target=DeepCloneableTarget{cloneName='cloneName', cloneClass='cloneClass'}}460141958 DeepPrototype{name='tom', target=DeepCloneableTarget{cloneName='cloneName', cloneClass='cloneClass'}}1791741888*/
3、原型模式的注意事项和细节
1)创建新对象比较复杂时,可以利用原型模式简化对象的创建,同时提高效率。
2)不用重新初始化对象,而是动态的获取对象运行时的状态。
3)如果原始对象发生变化(增加或减少对象),其他克隆对象就会随之变化,无需修改代码。
4)在实现深克隆时可能需要交复杂的代码。
5)缺点:需要为每个类配备一个克隆方法,这对全新的类不是难事,但是对已有的类进行改造时,需要修改源代码,违反了ocp原则。