设计模式之原型模式【Prototype Pattern】

原型模式是创建型模式,主要用来对原始对象进行拷贝。

1、定义

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

2、使用场景

  1. 类初始化需要消耗非常多的资源,资源包括数据、硬件资源等,通过原型复制避免资源消耗。
  2. 通过 new 生成对象需要非常繁琐的数据准备或访问权限。
  3. 一个对象需要提供给其他对象访问,但是其他对象可能对其修改时。可以使用原型模式复制多个对象供调用者使用。即保护性拷贝。

具体实现时,使用 new 还是 Cloneable的 clone() 要根据实际情况来定:当通过 new 构造对象较为耗时或者成本较高时,通过 clone 方法才能获得效率上的提升。

3、UML图

这里写图片描述

  1. Prototype:抽象类或接口,申明一个克隆自身的接口。
  2. ConcretePrototype :具体的原型类
  3. Client:调用者

4、示例代码

1. 通过自定义接口实现

/**
 * 申明一个克隆自身的接口
 */
public interface Prototype {

    public Prototype clone();

}

public class ConcretePrototype implements Prototype {

    //真正实现克隆自身的方法
    @Override
    public Prototype clone() {
        ConcretePrototype prototype = new ConcretePrototype();
        return prototype;
    }
}

public class Client {

    Prototype prototype;

    public Client(Prototype p) {
        prototype = p;
    }

    public void operation() {
        //克隆一个newPrototype
        Prototype newPrototype = prototype.clone();
    }
}

2. 通过实现java的 Cloneable 接口实现
这种方式只需要实现 Cloneable 接口,重写 clone() 就可以了。

public class ConcretePrototype implements Cloneable {

    @Override
    protected ConcretePrototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype) super.clone();
    }
}

5、浅拷贝和深拷贝

public class ConcretePrototype implements Cloneable {

    private String str;
    private List<String> list = new ArrayList<String>();

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public List<String> getList() {
        return list;
    }

    public void addString(String str) {
        this.list.add(str);
    }

    @Override
    protected ConcretePrototype clone() throws CloneNotSupportedException {
        ConcretePrototype p = (ConcretePrototype) super.clone();
        p.str = this.str;
        p.list = this.list;
        return p;
    }
}

对于以上的这个实现,其实是是一个浅拷贝(影子拷贝),并没有将原始对象的所有字段都重新构造一份,而是直接指向了原始对象字段的引用,也就是拷贝对象的字段引用了原始对象的字段。它们指向的是同一个对象。在修改拷贝对象的list时,会对原始对象的list造成影响。那该如何写才不会造成影响呢?

答案就是采用深拷贝的写法,在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯的引用形式。clone( ) 修改如下:

@Override
protected ConcretePrototype2 clone() throws CloneNotSupportedException {
    ConcretePrototype2 p = (ConcretePrototype2) super.clone();
    p.str = this.str;
    //p.list = this.list;
    //对于list对象,也调用 clone() 进行拷贝
    p.list = (ArrayList<String>) this.list.clone();
    return p;
}

原型模式的核心问题就是对原始对象进行拷贝,拷贝时要注意深、浅拷贝问题。
在开发过程中,为减少错误,应尽量使用深拷贝,避免对原始对象造成影响。

6、Android源码中实现

Android 中 Intent 类就实现了原型模式。

public class Intent implements Parcelable, Cloneable {
    /**
     * 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);
        }
    }

    @Override
    public Object clone() {
        return new Intent(this);
    }
    //省略其他代码
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值