java设计模式—原型模式(prototype pattern)
我们在程序开发过程中,都会遇到为一个类创建多个实例的情况,实例的创建开销很大,而且这些实例内部成员往往完全相同或仅有细微的差异,这种情况下如果能通过复制一个已创建的对象实例来重复创建多个相同的对象,这就可以大大减少创建对象的开销,此时就需要使用到原型模式。
1. 定义
原型模式是可以通过一个对象实例确定创建对象的种类,并且通过拷贝创建新的实例。总得来说,原型模式实际上就是从一个对象创建另一个新的对象,使新的对象有具有原对象的特征。这里提到了拷贝的概念,我们优先简单介绍下拷贝。
如果我们想复制一个变量的话是非常简单的,如下代码:
int i =10;
int j =i;
对于变量的复制(拷贝)不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。
但是针对对象的拷贝复制就没有这样简单了,我们以简单的代码示例:
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test1 {
public static void main(String[] args) {
Student a = new Student();
a.setAge(10);
a.setName("A");
System.out.println(a.getName());
Student b = a;
System.out.println(b.getName());
b.setName("B");
System.out.println(a.getName());
System.out.println(b.getName());
}
}
我们运行以上代码输出结果如下:
A
A
B
B
通过结果我们可以了解到在改变b的name的值时,a也跟着发生了改变,这是为什么吗?这是因为对象赋值过程与变量赋值的不同导致的,对象赋值仅仅是将对象的内存地址赋值给了另一个引用变量,既两个对象变量都是指向了同一个内存地址的对象,所以在改变其中一个变量指向的对象的属性时另一个也会跟着变化,针对对象的这种操作就出现了拷贝的概念,而拷贝又分为浅拷贝和深拷贝。
1.1 浅拷贝
在上面的student对象a中有两个属性,如下图所示:
由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝,上图就是钱拷贝的原理
1.2 深拷贝
深拷贝的原理如下图所示:
现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的。
2. UML类图
原型模式是基于对象的拷贝的,可以是浅拷贝也可以是深拷贝操作。也就是说当我们需要批量生成某一对象,就可以事先创建一个对象的原型,再通过对象的拷贝操作批量生成对象。原型模式的实现类图如下:
原型模式是一种应用及其广泛的设计模式,Clone也是一种十分常见的操作,以至于在Java中,终极父类Object将Clone方法作为了所有类应具有的基本功能,并且Java也提供了Cloneable接口
3. 简单代码实现
接下来举一个简单的代码示例,我们考虑一个写简历的场景,简历中包括的信息有姓名、性别、年龄、家庭成员和工作经验几点内容。现在的需求为由于需要向多个公司投递建立简历,因此子创建了一份建立对象之后,还要求能够对已创建的简历进行复制。此时,恰好有几个另外有几个同学也想找工作,为了方便,就把已创建的作为模版,然后根据自身的情况作了一些修改。
为了实现上述要求,我们首先定义一个工作经验类,里面有两个成员变量分别为工作时间和公司名称,代码如下:
//首先定义一个简历类
public class Resume implements Cloneable {
public String name = null;
public int age = 0;
public String sex = null;
public WorkExperience work = null;
public Resume(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
work = new WorkExperience();
}
public void setName(String name) {
this.name = name;
}
public void setWork(String timeArea, String company) {
work.timeArea = timeArea;
work.company = company;
}
/**
* 重些clone()方法为public类型,为每个字段都创建新的对象,已实现深拷贝功能。
*/
@Override
public Resume clone() throws CloneNotSupportedException {
int age = this.age;
String sex = this.sex;
String name = new String(this.name);
Resume copy = new Resume(name, age, sex);
copy.setWork(this.work.timeArea, this.work.company);
return copy;
}// clone
public void display() {
System.out.println(this.name + " " + this.sex + " " + this.age);
System.out.println();
System.out.print("Work experience: " + this.work.timeArea);
System.out.println(" " + this.work.company);
}// display
}/*Resume*/
class WorkExperience {
public String timeArea = null;
public String company = null;
}
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建初始简历
Resume resume1 = new Resume("Jobs", 25,"女");
resume1.setPersonal("Male", 26);
resume1.setWork("2013/8/1 - 2015/6/30", "Huawei");
// 通过简历1复制出简历2
Resume resume2 = resume1.clone();
resume2.setName("Tom");
resume2.setWork("2015/7/1 - 2016/6/30", "Baidu");
resume1.display();
resume2.display();
}// main
}/*Pritotype*/