设计模式之原型模式

原型设计模式的是一种创建型的设计模式,如果创建过程笔记复杂耗时的话,可以考虑使用原型设计模式;通过克隆已有的对象来获取一个新的对象,你可以直接使用它,也可以修改克隆对象的部分属性再使用,使得我们的程序更加高效

使用场景
  • 如果一个类的初始化需要很多资源,这个资源包括数据或者硬件资源等,通过原型拷贝已有的对象来避免这些消耗
  • 通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
  • 一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
如何实现

首先我们得实现Cloneable接口,复写clone方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.it.fengan.designpattern.pattern;

/**
* Created by fengan on 2017/12/22.
* email:fengan1102@gmail.com
*/

public class Worker implements Cloneable {
private String name;
private String des;
private double money;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDes() {
return des;
}

public void setDes(String des) {
this.des = des;
}

public double getMoney() {
return money;
}

public void setMoney(double money) {
this.money = money;
}

@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", des='" + des + '\'' +
", money=" + money +
'}';
}

@Override
public Worker clone() {
Worker worker = null;
try {
worker = (Worker) super.clone();
worker.money = this.money;
worker.name = this.name;
worker.des = this.des;

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return worker;
}
}

注意

  1. clone这个方法不是Cloneable接口中的(Cloneable是一个空接口)
  2. clone是Object中的方法
  3. Cloneable是一个标识接口,它表明这个类的对象是可以拷贝的
  4. 如果没有实现Cloneable接口却调用了clone()函数将抛出异常。
浅拷贝和深拷贝

浅拷贝

1
2
3
4
5
6
7
8
9
10
@Override
protected User clone() {
User user = null;
try{
user = (User)super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return user;
}

那么在实现clone方法的时候,需要注意个问题,像上面那样,直接调用

1
user = (User)super.clone();

这样只是简单的拷贝了对象,实际上并不是将原始文档的所有字段都重新构造了一份,而是副本文档的字段引用原始文档的字段。

浅拷贝

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
doc.mImages = (ArrayList<String>) this.mImages.clone();
return doc;
} catch (CloneNotSupportedException e) {

}
return null;
}
Android中的应用

Intenet源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
*
* <p>These are the possible flags that can be used in the Intent via
* {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list
* of all possible flags.
*/
public class Intent implements Parcelable, Cloneable {

...
@Override
public Object clone() {
return new Intent(this);
}

public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
}
...
}

可以见到,我们的Intent是深度拷贝,而且是直接new一个的深度拷贝。可见其设计初衷不是因为其不是为了解决构建复杂对象的资源消耗问题。

总结
  • 原型模式本质上就是对象拷贝,与 C++ 中的拷贝构造函数有些类似,它们之间容易出现的问题也都是深拷贝、浅拷贝。使用原型模式可以解决构建复杂对象的资源消耗问题,能够在某些场景下提升创建对象的效率。还有一个重要的途径就是保护性拷贝,也就是某个对象对外可能是只读的,为了防止外部对这个只读对象修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。
  • 优点:原型模式是在内存中二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好滴体现其优点。
  • 缺点:这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题
文章目录
  1. 1. 使用场景
  2. 2. 如何实现
  3. 3. 浅拷贝和深拷贝
  4. 4. Android中的应用
  5. 5. 总结