原型模式(Prototype) —— 就是复制那回事
一、什么是原型模式?
原型模式的核心思想是,通过拷贝指定的“原型实例(对象)”,创建跟该对象一样的新对象。简单理解就是“克隆指定对象”。
我看网上都是用简历来举例子,挺形象的那我们也用简历吧,都知道要针对不同的公司准备不同的简历,那一个人可能会有不同的简历:
public class Resume {
private String name;
private String position;
private int salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
。。。。。。
}
}
public static void main(String[] args) {
int num = 5;
while (num > 0){
Resume resume1 = new Resume();
int salary = (int)(1000+Math.random()*(2000-1000+1));
resume1.setName("小赵");
resume1.setPosition("高级铸剑工程师");
resume1.setSalary(salary);
System.out.println(resume1.toString());
num --;
}
}
这里是按正常的思路去写的,但每生成一份简历就需要 new 一个简历对象。
直观的感觉就是这样不好,为啥?首先,不停的new对象那么JVM就不停的分配内存,那么GC的频率可能会增加,影响效率;另外,可以看到每个简历只是工资部分在变化,其他信息没动,为了改个工资就要new个新的对象出来,有点“奢侈”了。
这个时候原型模式就派上用场了,原型模式不是每次都new,而是先new一个原型出来,后面的都是由这个原型进行复制创建,再局部调整,如下:
public class Resume implements Cloneable{
private String name;
private String position;
private int salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
protected Resume clone(){
Resume resume = null;
try{
resume = (Resume) super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return resume;
}
@Override
public String toString() {
return "Resume{" +
。 。。。。。
}
}
public static void main(String[] args) {
int num = 5;
Resume resume = new Resume();
while (num > 0){
Resume resume1 = resume.clone();
int salary = (int)(1000+Math.random()*(2000-1000+1));
resume1.setName("小赵");
resume1.setPosition("高级铸剑工程师");
resume1.setSalary(salary);
System.out.println(resume1.toString());
num --;
}
}
可以看到,这里简历类实现了Cloneable接口,在客户端调用的时候,也是调用clone()方法进行复制。
所以实现Cloneable接口,调用其clone()方法就能实现原型模式。
二、原型模式的使用场景
在哪些场景下可以使用上面的原型模式?
其实上面的说明已经大概介绍了使用场景,具体的:
在需要一个类的大量对象的时候,使用原型模式是最佳选择,因为原型模式是在内存中对这个对象进行拷贝,要比直接new这个对象性能要好很多,在这种情况下,需要的对象越多,原型模式体现出的优点越明显。
如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。
当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。
所以,通过原型模式可以简化创建重量级对象的过程,并提高程序的效率;使用原型设计模式可以使代码变的更加灵活,因为当原型类发生变化(增、减属性)时,克隆的对象也会做出相应的改变。但是,原型模式对已经创建好的类进行改造,使其支持克隆时需要修改源代码,这就是违背了ocp原则。不过,如果符合上面的三个场景时,还是建议使用原型模式的。
三、原型模式和享元模式
这两种模式目标是比较相似的,都是尽可能减少对象的创建,防止创建大量的对象。提升内存资源利用率及效率。
区别是:
享元模式要求对象完全一样,对象不一样就无法共享。
原型模式是要求对象大部分公共属性是一样的,个别属性不一致,拷贝一个原始原型对象,再进行微调。
四、深拷贝和浅拷贝
原型模式是借助对象拷贝实现的,那不可避免的要说到深拷贝和浅拷贝,可以参考详细介绍: