一、什么是原型模式
Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用Prototype模式创建的实例,具有与原型一样的数据。
二、原型模式的特点
由原型对象自身创建目标对象。也就是说,对象创建这一动作发自原型对象本身。
目标对象是原型对象的一个克隆。也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
三、原型模式应用场景
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗;
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
- 在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
- 希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。
- 隐藏克隆操作的细节。很多时候,对对象本身的克隆需要涉及到类本身的数据细节。
如,有一个Person类
public class Person {
private String name;
private String gender;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
创建一个Person对象
Person person1 = new Person();
person1.setName("Jack");
person1.setGender("男");
person1.setAge(18);
当下次想再次创建一个person对象时,
Person person2 = new Person();
person2.setName("John");
person2.setGender("男");
person2.setAge(18);
person1与person2对象只有name不同,其他都相同,这个时候我们就可以使用原型模式,一般我们会提供一个clone方法,克隆不同于引用.
我们在Person类中添加clone方法,返回一个克隆的person对象
public Person clone() {
try {
Person person = (Person)super.clone();
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
注意Person类要实现Cloneable接口
public static void main(String[] args) {
Person person1 = new Person();
person1.setName("Jack");
person1.setGender("男");
person1.setAge(18);
Person person2 = person1.clone();
System.out.println("person1---name:"+person1.getName());
person2.setName("John");
System.out.println("person1---name:"+person1.getName());
System.out.println("person1---name:"+person2.getName());
}
输出结果为:
person1—name:Jack
person1—name:Jack
person2—name:John
现在我们给Person添加hobbies字段
private List<String> hobbies;
public static void main(String[] args) {
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("音乐");
hobbies.add("跑步");
Person person1 = new Person();
person1.setName("Jack");
person1.setGender("男");
person1.setAge(18);
person1.setHobbies(hobbies);
Person person2 = person1.clone();
System.out.println("person2---hobbies:"+person2.getHobbies().toString());
System.out.println("person1---hobbies:"+person1.getHobbies().toString());
hobbies.add("打球");
System.out.println("person2---hobbies:"+person2.getHobbies().toString());
System.out.println("person1---hobbies:"+person1.getHobbies().toString());
}
输入结果
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步]
person2---hobbies:[音乐, 跑步, 打球]
person1---hobbies:[音乐, 跑步, 打球]
我们修改了person1的hobbies字段,person2的也跟着该变了,这就是浅度克隆
什么是深度克隆呢?
修改clone方法
public Person clone(){
try {
Person person = (Person)super.clone();
ArrayList<String> newHobby = new ArrayList<String>();
for(String hobby : this.getHobbies()) {
newHobby.add(hobby);
}
person.setHobbies(newHobby);
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
再次运行
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步]
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步, 打球]
Android中的SparseArray类使用了原型模式,SparseArray类用来代替HashMap
public class SparseArray<E> implements Cloneable {
...
}
clone方法,
@Override
@SuppressWarnings("unchecked")
public SparseArray<E> clone() {
SparseArray<E> clone = null;
try {
clone = (SparseArray<E>) super.clone();
clone.mKeys = mKeys.clone(); //同时需要深度克隆
clone.mValues = mValues.clone();//同时需要深度克隆
} catch (CloneNotSupportedException cnse) {
/* ignore */
}
return clone;
}
结合上面的应用场景理解一下SparseArray类为什么使用原型模式。