原型模式

原型模式

一、简介

该模式多用于创建复杂的或者构造耗时的实例。

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

二、原型模式的使用场景

  • 如果类的初始化需要耗费较多的资源,那么可以通过原型拷贝避免这些消耗。
  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以拷贝多个对象供调用者使用,即保护性拷贝。

三、原型模式模式简单实现

    public static class WordDocument implements Cloneable{
        private String text;
        private ArrayList<String> images=new ArrayList<>();

        public WordDocument() {
        }

        public String getText() {
            return text == null ? "" : text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public ArrayList<String> getImages() {
            if (images == null) {
                return new ArrayList<>();
            }
            return images;
        }

        public void setImages(String images) {
            this.images.add(images);
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            try {
                WordDocument document= (WordDocument) super.clone();
                document.text=this.text;
                //浅拷贝
                document.images=this.images;
                //深拷贝
//                document.images= (ArrayList<String>) this.images.clone();
                return document;
            }catch (Exception e){

            }
            return null;
        }

        @Override
        public String toString() {
            return "WordDocument{" +
                    "text='" + text + '\'' +
                    ", images=" + images +
                    '}';
        }
    }

    public static void cloneWordDocument() throws CloneNotSupportedException {
        WordDocument origindoc=new WordDocument();
        origindoc.setText("the first doc");
        origindoc.setImages("image1");
        origindoc.setImages("image2");
        Log.i(TAG, "cloneWordDocument: origin "+origindoc.toString());

        WordDocument clonedoc= (WordDocument) origindoc.clone();
        clonedoc.setText("the second doc");
        Log.i(TAG, "cloneWordDocument: clone "+clonedoc.toString());
        Log.i(TAG, "cloneWordDocument: origin "+origindoc.toString());
    }

上述log如下所示:

image-20200812131853309

可以看出clone的对象改变text值并不会影响origin。但是如果我们为clone添加一个images这时origin也会受到影响,这是因为上面对images的拷贝是基于浅拷贝的。

克隆出来的新对象的images没有重新构造出新的images而是引用origin的images,所以两者的images实际是指向同一对象的,要解决这个问题我们需要使用深拷贝,即对引用类型的字段也要调用其clone方法进行克隆。

四、Android中原型模式的使用

Intent是我们经常使用的一个类,它提供了clone方法,我们一起来看下

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

可以看出Intent内部的clone方法并没有调用super.clone来拷贝对象,而是new了一个新的实例。其实使用clone还是new需要根据实际情况来决定,如果构造新的实例的成本比较高那么就可以使用clone,否则可以使用new。

posted @ 2020-10-09 18:35  Robin132929  阅读(134)  评论(0编辑  收藏  举报