一、原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模型其实是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
类型:创建型类型
优点和使用场景:1.使用原型模型创建对象比直接new一个对象在性能上要好的多,原因是Object的clone方法是一个本地方法,直接操作内存中的二进制流。2.简化对象的创建。
注意事项-深拷贝和浅拷贝:
Object的clone方法只会拷贝对象中的基本数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。要实现深拷贝,需要在重写clone方法时,将原型模型中的数组、容器对象和引用对象等进行拷贝。
二、UML类图
原型类Protype需具备以下两个条件:
1.实现Cloneable接口。java语言的cloneable接口的作用只有一个:在运行时通知虚拟机可以安全地在此接口的类上使用clone方法。在java虚拟机中,只有实现这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException。
2.重写Object类的clone方法。Java中所有类的父类是Object类,Object类中有一个clone方法,是本地方法,作用时返回对象的一个拷贝,但作用域是protected,因此Protype类需要将clone方法的作用域变为public。
Protype实现一个接口,并重写clone方法,即可完成原型模式。实际应用中,原型模型很少单独出现,经常与其他模型混用,因此Protype类常用抽象类替代。
三、具体案例
参考大话设计模式-简历的复制。
浅复制:
java实现代码:
public class PrototypeDesign {
public static void main(String[] args){
Resume resume=new Resume("ff");
resume.setPersonalInfl(24, "男");
resume.setWorkExperience("2017-05-02", "mt");
resume.display();
//resume2 拷贝了resume
Resume resume2=resume.clone();
resume2.setPersonalInfl(20, "女");
resume2.setWorkExperience("2017-05-02", "didi");
//对resume2进行修改,观察resume和resume2的变化
System.out.println("对resume2修改后:");
resume2.display();
resume.display();
}
}
class Resume implements Cloneable{
private String name;
private int age;
private String sex;
private Experience experience;
public Resume(String name){
this.name=name;
this.experience=new Experience();
}
public void setPersonalInfl(int age,String sex){
this.age=age;
this.sex=sex;
}
public void setWorkExperience(String timeArea,String company){
this.experience.timeArea=timeArea;
this.experience.company=company;
}
public void display(){
System.out.println(name+" "+sex+" "+age);
System.out.println(experience.timeArea+" "+experience.company);
}
public Resume clone() {
Resume resume=null;
try{
resume=(Resume)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
}
class Experience{
public String timeArea;
public String company;
}
实现结果:
ff 男 24 2017-05-02 mt 对resume2修改后: ff 女 20 2017-05-02 didi ff 男 24 2017-05-02 didi |
结果分析:
程序先创建了一个resume对象,resume2对象对其进行了拷贝并修改了性别(String)、年龄(int)、工作经历(Experience),结果resume对象的工作经历也被修改了。由此证明了
浅拷贝只拷贝对象的基本类型,而不拷贝对象包含的引用指向的对象。
深拷贝:
java实现代码:
public class PrototypeDesign {
public static void main(String[] args){
Resume resume=new Resume("ff");
resume.setPersonalInfl(24, "男");
resume.setWorkExperience("2017-05-02", "mt");
resume.display();
//resume2 拷贝了resume
Resume resume2=resume.clone();
resume2.setPersonalInfl(20, "女");
resume2.setWorkExperience("2017-05-02", "didi");
//对resume2进行修改,观察resume和resume2的变化
System.out.println("对resume2修改后:");
resume2.display();
resume.display();
}
}
class Resume implements Cloneable{
private String name;
private int age;
private String sex;
private Experience experience;
public Resume(String name){
this.name=name;
this.experience=new Experience();
}
public void setPersonalInfl(int age,String sex){
this.age=age;
this.sex=sex;
}
public void setWorkExperience(String timeArea,String company){
this.experience.timeArea=timeArea;
this.experience.company=company;
}
public void display(){
System.out.println(name+" "+sex+" "+age);
System.out.println(experience.timeArea+" "+experience.company);
}
public Resume clone() {
Resume resume=null;
try{
resume=(Resume)super.clone();
resume.experience=resume.experience.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
}
class Experience implements Cloneable{
public String timeArea;
public String company;
public Experience clone(){
Experience experience=null;
try {
experience=(Experience)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return experience;
}
}
实现结果:
ff 男 24 2017-05-02 mt 对resume2修改后: ff 女 20 2017-05-02 didi ff 男 24 2017-05-02 mt |
结果分析:
将Resume类的成员变量Experience也实现Cloneable接口并重新clone方法,同时在Resume类的clone方法中,对Experience引用对象进行另外拷贝,从而实现了深拷贝。
参考:
2.大话设计模式