原型模式Prototype

原型模式定义

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

原型模式类图

类图说明

  • Client:客户端用户。
  • Prototype:抽象类或者接口,声明具备Clone能力。
  • ConcretePrototype:具体的原型类。

原型模式的优点

  • 性能优良:
    • 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
  • 逃避构造函数的约束:
    • 这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。因此,如果在构造函数中需要一些特殊的初始化操作的类型,在使用Cloneable实现拷贝时,需要注意构造函数不会执行的问题。

原型模式的使用场景

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

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

3、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值,同时有可能每个对象中有一些共有属性时,可以考虑使用原型模式拷贝多个对象提供调用者使用,即保护性拷贝。

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

原型模式的简单实现

// 简单的可拷贝对象
public class Thing implements Cloneable {
    // 构造函数,在拷贝对象时不会被调用
    public Thing() {
        System.out.println("构造函数被执行了...");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
}

// 简单的场景类
public class Client {
    public static void main(String[] args) {
        // 产生一个对象
        Thing thing = new Thing();
        // 拷贝一个对象
        Thing cloneThing = thing.clone();
    }
}

运行结果:

构造函数被执行了...

浅拷贝

Object类提供的方法clone只是拷贝本对象(和其中的全局原始类型,比如int、long、char等,还有String),其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址。

浅拷贝的例子:

public class Thing implements Cloneable {
    // 定义一个私有变量
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
    
    //设置arrayList的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }
    
    // 取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

public class Client {
    public static void main(String[] args) {
        // 产生一个对象
        Thing thing = new Thing();
        // 设置一个值
        thing.setValue("张三");
        // 拷贝一个对象
        Thing cloneThing = thing.clone();
        cloneThing.setValue("李四");
        System.out.println(thing.getValue());
    }
}

运行结果:

[张三, 李四]

因为通过clone拷贝出来的cloneThing对象中的arrayList没有被单独拷贝,和thing对象共用一份arrayList,所以改变了cloneThing对象中的arrayList也就改变了thing对象中的arrayList。

深拷贝

进行深度的完全拷贝。两个对象之间没有任何的瓜葛了。

深拷贝的例子

public class Thing implements Cloneable {
    // 定义一个私有变量
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
            // 对arrayList也进行拷贝
            thing.arrayList = (ArrayList<String>) this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
    
    //设置arrayList的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }
    
    // 取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

public class Client {
    public static void main(String[] args) {
        // 产生一个对象
        Thing thing = new Thing();
        // 设置一个值
        thing.setValue("张三");
        // 拷贝一个对象
        Thing cloneThing = thing.clone();
        cloneThing.setValue("李四");
        System.out.println(thing.getValue());
    }
}

运行结果:

[张三]

因为对arrayList也进行了独立的拷贝,所以修改cloneThing对象中的arrayList并不会影响thing对象中的arrayList值。

clone与final

对象的clone与对象内的final关键字是有冲突的。比如上面的例子中,如果arrayList有final属性,在clone处,编译器会报错。

所以,要使用clone方法,类的成员变量上不要增加final关键字。

Android中的应用

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

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

/**
 * Copy constructor.
 */
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);
    }
}

 

转载于:https://my.oschina.net/u/3026396/blog/815833

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值