结合Android学设计模式--原型模式(Prototype)

###一、什么是原型模式
Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用Prototype模式创建的实例,具有与原型一样的数据。
这里写图片描述
###二、原型模式的特点

  1. 由原型对象自身创建目标对象。也就是说,对象创建这一动作发自原型对象本身。
  2. 目标对象是原型对象的一个克隆。也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
  3. 根据对象克隆深度层次的不同,有浅度克隆深度克隆。

###三、原型模式应用场景

  • 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗;
  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
  • 在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
  • 希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。
  • 隐藏克隆操作的细节。很多时候,对对象本身的克隆需要涉及到类本身的数据细节。

如,有一个Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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对象

1
2
3
4
      Person person1 = new Person();
person1.setName("Jack");
person1.setGender("男");
person1.setAge(18);

当下次想再次创建一个person对象时,

1
2
3
4
      Person person2 = new Person();
person2.setName("John");
person2.setGender("男");
person2.setAge(18);

person1与person2对象只有name不同,其他都相同,这个时候我们就可以使用原型模式,一般我们会提供一个clone方法,克隆不同于引用.
我们在Person类中添加clone方法,返回一个克隆的person对象

1
2
3
4
5
6
7
8
9
public Person clone() {
try {
Person person = (Person)super.clone();
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}

注意Person类要实现Cloneable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
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字段

1
private List<String> hobbies;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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());
}

输入结果

1
2
3
4
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步]
person2---hobbies:[音乐, 跑步, 打球]
person1---hobbies:[音乐, 跑步, 打球]

我们修改了person1的hobbies字段,person2的也跟着该变了,这就是浅度克隆
什么是深度克隆呢?

修改clone方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
}

再次运行

1
2
3
4
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步]
person2---hobbies:[音乐, 跑步]
person1---hobbies:[音乐, 跑步, 打球]

Android中的SparseArray类使用了原型模式,SparseArray类用来代替HashMap

1
2
3
public class SparseArray<E> implements Cloneable {
...
}

clone方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
@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类为什么使用原型模式