一、原型模式定义
1.原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建型模式。
2.原型模式的核心在于拷贝原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时的对象初始化过程(不调用构造函数),性能提升许多。当对象的构建过程比较耗时时,可以利用当前系统中已存在的对象作为原型,对其进行克隆(一般是基于二进制流的复制),躲避初始化过程,使得新对象的创建时间大大减少。
3.原型模式主要包含三个角色:
A.客户(Client):客户类提出创建对象的请求
B.抽象原型(Prototype):规定拷贝接口
C.具体原型(Concrete Prototype):被拷贝的对象
4.原型模式主要适用于以下场景:
A.类初始化消耗资源较多
B.new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
C.构造函数比较复杂
D.循环体中生产大量对象
二、原型模式的通用写法
1 public interface IPrototype<T> { 2 T clone(); 3 } 4 5 /** 6 * 具体需要克隆的对象 7 */ 8 public class ConcretePrototype implements IPrototype { 9 10 private int age; 11 private String name; 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 @Override 30 public ConcretePrototype clone() { 31 ConcretePrototype concretePrototype = new ConcretePrototype(); 32 concretePrototype.setAge(this.age); 33 concretePrototype.setName(this.name); 34 return concretePrototype; 35 } 36 37 @Override 38 public String toString() { 39 return "ConcretePrototype{"+ 40 "age=" + age + 41 ", name='" + name + '\'' + 42 '}'; 43 } 44 }
2.jdk自带的写法
1 /** 2 * 具体需要克隆的对象 3 */ 4 public class JdkConcretePrototype implements Cloneable { 5 6 private int age; 7 private String name; 8 9 public int getAge() { 10 return age; 11 } 12 13 public void setAge(int age) { 14 this.age = age; 15 } 16 17 public String getName() { 18 return name; 19 } 20 21 public void setName(String name) { 22 this.name = name; 23 } 24 25 @Override 26 public JdkConcretePrototype clone() { 27 try { 28 return (JdkConcretePrototype)super.clone(); 29 }catch (CloneNotSupportedException e){ 30 e.printStackTrace(); 31 return null; 32 } 33 } 34 35 @Override 36 public String toString() { 37 return "JdkConcretePrototype{"+ 38 "age=" + age + 39 ", name='" + name + '\'' + 40 '}'; 41 } 42 }
3.给ConcretePrototype增加一个个人爱好的属性hobbies:
1 /** 2 * 具体需要克隆的对象 3 */ 4 public class JdkConcretePrototype implements Cloneable { 5 6 private int age; 7 private String name; 8 private List<String> hobbies; 9 10 public int getAge() { 11 return age; 12 } 13 14 public void setAge(int age) { 15 this.age = age; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public List<String> getHobbies() { 27 return hobbies; 28 } 29 30 public void setHobbies(List<String> hobbies) { 31 this.hobbies = hobbies; 32 } 33 34 @Override 35 public JdkConcretePrototype clone() { 36 try { 37 return (JdkConcretePrototype)super.clone(); 38 }catch (CloneNotSupportedException e){ 39 e.printStackTrace(); 40 return null; 41 } 42 } 43 44 @Override 45 public String toString() { 46 return "JdkConcretePrototype{"+ 47 "age=" + age + 48 ", name='" + name + '\'' + 49 ", hobbies=" + hobbies + 50 '}'; 51 } 52 } 53 54 public class JdkPrototypeTest { 55 public static void main(String[] args) { 56 //创建原型对象 57 JdkConcretePrototype prototype = new JdkConcretePrototype(); 58 prototype.setName("Tom"); 59 prototype.setAge(18); 60 List<String> hobbies = new ArrayList<String>(); 61 hobbies.add("美术"); 62 prototype.setHobbies(hobbies); 63 System.out.println(prototype); 64 65 //拷贝原型对象 66 JdkConcretePrototype cloneType = prototype.clone(); 67 cloneType.getHobbies().add("技术控"); 68 69 System.out.println("原型对象:" + prototype); 70 System.out.println("克隆对象:" + cloneType); 71 } 72 }
运行后发现,给复制后的克隆对象新增一项爱好,原型对象也发生了变化,不符合我们的预期。hobbies共用了一个内存地址,意味着复制的不是值,而是引用的地址,这即是我们常说的浅克隆。
4.使用序列化实现深度克隆
1 /** 2 * 具体需要克隆的对象 3 */ 4 public class JdkConcretePrototype implements Cloneable, Serializable { 5 6 private int age; 7 private String name; 8 private List<String> hobbies; 9 10 public int getAge() { 11 return age; 12 } 13 14 public void setAge(int age) { 15 this.age = age; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public List<String> getHobbies() { 27 return hobbies; 28 } 29 30 public void setHobbies(List<String> hobbies) { 31 this.hobbies = hobbies; 32 } 33 34 @Override 35 public JdkConcretePrototype clone() { 36 try { 37 return (JdkConcretePrototype)super.clone(); 38 }catch (CloneNotSupportedException e){ 39 e.printStackTrace(); 40 return null; 41 } 42 } 43 44 public JdkConcretePrototype deepClone(){ 45 try { 46 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 47 ObjectOutputStream oos = new ObjectOutputStream(bos); 48 oos.writeObject(this); 49 50 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 51 ObjectInputStream ois = new ObjectInputStream(bis); 52 53 return (JdkConcretePrototype)ois.readObject(); 54 }catch (Exception e){ 55 e.printStackTrace(); 56 return null; 57 } 58 } 59 60 @Override 61 public String toString() { 62 return "JdkConcretePrototype{"+ 63 "age=" + age + 64 ", name='" + name + '\'' + 65 ", hobbies=" + hobbies + 66 '}'; 67 } 68 }
5.克隆破坏单例模式,单例类不实现Cloneable接口
6.原型模式的优缺点
A.优点
a.性能优良,java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多
b.可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作
B.缺点
a.需要为每一个类配置一个克隆方法
b.克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则
c.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来较为麻烦。