Android设计模式之原型模式

 

 

原型模式的定义:

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

 

原型模式的使用场景:

1.类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等,通过原型复制避免这些消耗

2.通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。

3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用,即保护性拷贝

 

需要注意的是,通过实现Cloneable接口的原型模式在调用clone函数构造实例并不一定比通过new操作速度快只有当通过new构造对象较为耗时时或者说成本较高时通过clone方法才能够获得效率上的提升。因此,在使用Cloneable时需要考虑创建对象的成本以及做一些效率上的尝试。当然,实现原型模式也不一定非要实现Cloneable接口。

 

 

 

 

需要注意的是:clone拷贝对象时并不会执行构造函数

注意:

clone方法用于实现对象克隆,注意,这个方法并不是Cloneable接口中的,而是Object中的方法。Cloneable也是一个标识接口,它表明这个类的对象是可拷贝的。如果没有实现Cloneable接口却调用了clone()函数将抛出异常

 

 

 

protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

浅拷贝和深拷贝

上述原型模式的实现实际上是一个浅拷贝,也称为影子拷贝。这份拷贝实际上并不是将原始文档的所有字段都重新构造一份,而是副本文档的字段引用原始文档的字段

上图表示浅拷贝

我们知道A引用B就是两个对象指向同一个地址,当修改A时B也会改变,B修改时,A同样会改变。所以需要采用深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯的引用形式。

Android源码中的原型模式的实现

首先观察一下java中的ArrayList;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ 

//元素数量,也就是这个列表的大小
private int size;

//实际存储数据的数组
transient Object[] elementData;

//返回一个副本对象
public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}
 }

上述代码很简单,clone方法首先克隆自身对象,然后再克隆存储元素的数组。大家可能发现了,size并没有像array一样被克隆?这是为什么?原因就是size是整型,并不是一个对象,它是值类型,而不是引用类型,因此,不需要进行克隆。在将副本对象克隆好之后,直接分返给客户端对象即可。

 

在Android中,Intent可能是我们最早接触的几个类型之一,它用于跳转Activity、启动服务、发布广播等功能,它是Android系统各组件之间的纽带,也是组件之间传递数据的载体,正是Intent的存在才使得Android各个组件之间的耦合性很低,Android的组件才如此灵活。

Uri uri = Uri.parse("smsto:0800000123");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);
shareIntent.putExtra("sms_body", "The Sms text");
//克隆副本
Intent intent = (Intent) shareIntent.clone();
startActivity(intent);

 

上述代码中首先构建一个 发送短信的Intent对象,并且设置了短信内容,然后通过原始Intent的clone()方法构建一个副本Intent,再使用副本Intent进行跳转。

 

#Intent

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


/**
 * Copy constructor.
 */
public Intent(Intent o) {
    this(o, COPY_MODE_ALL);
}


//拷贝构造函数
private Intent(Intent o, @CopyMode int copyMode) {
    this.mAction = o.mAction;
    this.mData = o.mData;
    this.mType = o.mType;
    this.mPackage = o.mPackage;
    this.mComponent = o.mComponent;

    if (o.mCategories != null) {
        this.mCategories = new ArraySet<>(o.mCategories);
    }

    if (copyMode != COPY_MODE_FILTER) {
        this.mFlags = o.mFlags;
        this.mContentUserHint = o.mContentUserHint;
        this.mLaunchToken = o.mLaunchToken;
        if (o.mSourceBounds != null) {
            this.mSourceBounds = new Rect(o.mSourceBounds);
        }
        if (o.mSelector != null) {
            this.mSelector = new Intent(o.mSelector);
        }

        if (copyMode != COPY_MODE_HISTORY) {
            if (o.mExtras != null) {
                this.mExtras = new Bundle(o.mExtras);
            }
            if (o.mClipData != null) {
                this.mClipData = new ClipData(o.mClipData);
            }
        } else {
            if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
                this.mExtras = Bundle.STRIPPED;
            }

            // Also set "stripped" clip data when we ever log mClipData in the (broadcast)
            // history.
        }
    }
}

 

 

可以看到clone方法实际上在内部并没有调用super.clone()方法来实现对象拷贝,而是调用了new Intent(this)。在上文我们提到过,使用clone和new需要根据构造对象的成本来决定,如果对象的构造成本比较高或者构造较为麻烦,那么使用clone()函数效率较高,否则可以使用new的形式。

 

 

参考:《Android源码设计模式》

 

 

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/83713417