原型模式
原型模式
1.定义
- 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- 原型模式的结构
- 原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法获取新的对象,而无需通过new来创建。
-
原型模式涉及到三个角色:
- 客户角色
- 客户类提出创建对象的请求
- 抽象原型角色
- 这是一个抽象角色,通常由一个接口或一个抽象类实现,此角色给出所有具体原型类所需的接口。
- 具体原型角色
- 被复制的角色,此角色需要实现抽象原型角色所要求的的接口
- 客户角色
-
源代码
- 抽象原型角色
public abstract class Prototype implements Cloneable { public Prototype clone() { Prototype prototype = null; try { prototype = (Prototype)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return prototype; } public abstract void show(); }
- 具体原型角色
public class ConcretePrototype extends Prototype { public void show() { System.out.println("ConcretePrototype.show()"); } }
- 客户端创建对象
public class Client { public static void main(String[] args) { ConcretePrototype cp = new ConcretePrototype(); for (int i = 0; i < 10; i++) { ConcretePrototype clonecp = (ConcretePrototype)cp.clone(); clonecp.show(); } } }
2.为什么使用原型模式
- 原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
- 原型模式的优点
- 一般在初始化信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能有大的提高。
clone()
方法是一个native方法,它直接操作内存中的二进制流,不需要调用构造函数,只是内存中数据块的拷贝。- 每new一次,都需要执行一次构造函数,如果构造函数比较复杂,那么多次执行较为低效。
- 一般在初始化信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能有大的提高。
3.如何实现
-
需求:写一个简历类,需要有姓名,可以设置性别和年龄,可以设置相关从业经历。最终要实现三份简历。
-
简历代码实现
package cn.sun.code.nine;
/**
* 简历
*/
public class Resume {
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 级别
*/
private String level;
/**
* 公司
*/
private String company;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public void display() {
System.out.println(name + " " + sex + " ");
System.out.println("工作经历: " + company + " " + level);
}
// 客户端代码
public static void main(String[] args) {
Resume a = new Resume();
a.setName("卡卡西");
a.setSex("男");
a.setCompany("木叶");
a.setLevel("上忍");
Resume b = new Resume();
b.setName("卡卡西");
b.setSex("男");
b.setCompany("木叶");
b.setLevel("上忍");
Resume c = new Resume();
c.setName("卡卡西");
c.setSex("男");
c.setCompany("木叶");
c.setLevel("上忍");
a.display();
b.display();
c.display();
}
}//output:
/**
*
卡卡西 男
工作经历: 木叶 上忍
卡卡西 男
工作经历: 木叶 上忍
卡卡西 男
工作经历: 木叶 上忍
*/
- 三份简历就需要三次实例化,这样客户端代码就会很麻烦,如果二十份,就需要二十份实例化。如果有修改地方,就需要修改二十次。
- 可以将原型模式典型实现中的抽象原型角色和具体原型角色合二为一,即直接使用简历类实现
Cloneable
接口
package cn.sun.code.nine;
/**
* 简历可复制
*/
public class Resume2 implements Cloneable {
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 级别
*/
private String level;
/**
* 公司
*/
private String company;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void display() {
System.out.println(name + " " + sex + " ");
System.out.println("工作经历: " + company + " " + level);
}
// 客户端代码
public static void main(String[] args) throws CloneNotSupportedException {
Resume2 a = new Resume2();
a.setName("卡卡西");
a.setSex("男");
a.setCompany("木叶");
a.setLevel("上忍");
Resume2 b = (Resume2)a.clone();
b.setLevel("六代目火影");
a.display();
b.display();
}
}
/**
* 卡卡西 男
工作经历: 木叶 上忍
卡卡西 男
工作经历: 木叶 六代目火影
*/
简历类的深复制实现
- 关于深复制实现见另一篇文章
- 以下是简历类深复制的代码
package cn.sun.code.nine;
/**
* 简历类深复制
*/
/**
* 工作经历
*/
class WorkExperience implements Cloneable {
/**
* 时间
*/
private String time;
/**
* 单位
*/
private String company;
public WorkExperience(String time, String company) {
this.time = time;
this.company = company;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "WorkExperience{" +
"time='" + time + '\'' +
", company='" + company + '\'' +
'}';
}
}
public class Resume3 implements Cloneable {
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 工作经历
*/
private WorkExperience workExperience;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public WorkExperience getWorkExperience() {
return workExperience;
}
public void setWorkExperience(WorkExperience workExperience) {
this.workExperience = workExperience;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void display() {
System.out.println(name + " " + sex + " ");
System.out.println("工作经历: " + workExperience);
}
// 客户端代码
public static void main(String[] args) throws CloneNotSupportedException {
Resume3 a = new Resume3();
a.setName("卡卡西");
a.setSex("男");
WorkExperience workExperience = new WorkExperience("初期", "忍者学校");
a.setWorkExperience(workExperience);
Resume3 b = (Resume3) a.clone();
WorkExperience workExperienceb = (WorkExperience) workExperience.clone();
workExperienceb.setTime("中期");
workExperienceb.setCompany("四代直属暗部");
b.setWorkExperience(workExperienceb);
a.display();
b.display();
}
}
卡卡西 男
工作经历: WorkExperience{time='初期', company='忍者学校'}
卡卡西 男
工作经历: WorkExperience{time='中期', company='四代直属暗部'}
4.原型模式在Java中的应用
- ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
...
}
- 我们在使用中完全可以通过调用
arrayList.clone()
方法获取到原ArrayList的拷贝