原型模式(Prototype Pattern)

定义

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

使用场景

  • 初始化需要消耗非常多的资源的类。

  • 通过new创建一个对象需要非常繁琐的数据准备或访问权限。

  • 一个对象需要提供给别人访问,但又要避免调用者修改其本身的数据时。

优缺点

优点:

  • 原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好。

缺点:

  • 原型模式直接在内存中拷贝,类的构造函数是不会被执行的,在实际开发中需要注意这个潜在的问题。

UML类图

原型模式

  • Client:客户端用户

  • Prototype:原型接口或抽象类,声明具备clone能力。

  • ConcretePrototype:具体的原型类。

Java中实现原型模式

在Java中可通过以下两种方式来实现原型模式:

  • 实现Cloneable接口,通过其clone()来构造实例。

  • 通过new构造对象,并手动拷贝各成员变量的值。

使用clone()和new需要根据构造对象的成本来决定:如果对象的构造成本比较高或构造较为麻烦,那么使用clone()效率更高,否则使用new。

Java中的ArrayList类是使用第一种方式clone()来实现原型模式的。

public class ArrayList<E> implements Cloneable {
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

Android中的Intent类是使用第二种方式new来实现原型模式的。

public class Intent implements Cloneable {
   
    public Intent() {
    }

    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);
        }
    }

    @Override
    public Object clone() {
        return new Intent(this);
    }
}
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

浅拷贝和深拷贝

浅拷贝:拷贝原型对象时,只是复制了其引用类型的成员变量的值,新成员变量也是指向同一块内存。

深拷贝:拷贝原型对象时,申请一个新的内存,复制其引用类型的成员变量最终指向的内存数据。

public class Book implements Cloneable {
    private String title;
    private List<String> content;
    
    // setter和getter略
    
    // 1. 浅拷贝
    protected Book clone() {
        try {
            Book book = (Book) super.clone();
            book.title = this.title;
            book.content = this.content;
        } catch(Exception ex) {
        }
        return null;
    }
    
    // 2. 深拷贝
    public Book clone() {
        try {
            Book book = (Book) super.clone();
            book.title = this.title;
            book.content = (ArrayList<String>) this.content.clone();
        } catch(Exception ex) {
        }
        return null;
    }
}
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