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

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

1、定义

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

2、使用场景

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

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

3、UML图

设计模式之原型模式【Prototype Pattern】_第1张图片

  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 list = new ArrayList();

    public String getStr() {
        return str;
    }

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

    public List 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) 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(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);
    }
    //省略其他代码
}

你可能感兴趣的:(设计模式,Prototype,原型模式,设计模式)