###一、什么是原型模式
Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用Prototype模式创建的实例,具有与原型一样的数据。
###二、原型模式的特点
- 由原型对象自身创建目标对象。也就是说,对象创建这一动作发自原型对象本身。
- 目标对象是原型对象的一个克隆。也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
- 根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
###三、原型模式应用场景
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗;
- 通过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类为什么使用原型模式