Android源码设计模式——原型模式

本文深入探讨了设计模式中的原型模式,介绍了其概念、应用场景及实现方式,通过具体示例展示了如何利用原型模式高效地复制对象,特别是对于复杂对象的复制,避免了资源的过度消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇博客学习了Build模式,本篇博客学习一下原型模式,其实说到原型模式,刚开始听的时候一脸懵逼,但是学习完了之后,真的是感觉简单。

原型模式介绍

原型模式是创建型模式的一种,其实就是想要以某一个对象为“模版”,“复制”出相同的对象,也就是“克隆”出一摸一样的对象。原型模式多用于需要构建复杂的对象时使用。因为此时“复制”一个对象比创建new一个对象效率更高。

原型模式定义

用原型对象的实例执行创建对象的种类,并通过拷贝创建出新的对象

原型模式的使用场景

  1. 创建new一个对象需要消耗非常多的资源,这个资源包括数据,硬件的消耗等,通过复制拷贝的方式避免过的消耗资源
  2. 一个对象可能在多个地方使用,并且使用的地方都可能修改这个对象的属性值,此时应该拷贝若干份对象,给不同的调用者使用
  3. 通过new创建对象需要复杂的数据准备或者数据设置,这时可以直接拷贝复制对象。

一般来说,我们实现原型模式的方法有两种:

  1. 原型对象实现Cloneable接口,然后在clone方法内,调用父类的对象clone方法拷贝对象。
  2. 不使用Cloneable接口,自行在clone方法内处理逻辑

原型模式的简单实现(实现Cloneable接口)

示例:假设现在我们正在写一个文档Document,文档中包含图片和文字,用户写了很长的文档,此时需要修改里面的东西,但是呢,我们又不确定修改编辑后的内容是否会被采用,此时我们就可以拷贝一份文档Document,这就是典型的:原型模式。

原型

public class Document implements Cloneable{

    private String content; //内容

    private ArrayList<String> imageList = new ArrayList<>(); //图片

    public Document() {
        Log.e("Document:", "构造方法");
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public List<String> getImageList() {
        return imageList;
    }

    public void addImage(String image) {
        this.imageList.add(image);
    }

    @Override
    protected Document clone() {
        Document documentCopy = null;
        try{
            documentCopy = (Document) super.clone();
            documentCopy.content = this.content;
            documentCopy.imageList = this.imageList;
        } catch (CloneNotSupportedException e) {

        }
        return documentCopy;
    }

    @Override
    public String toString() {
        String diver = "\n------------start-------------\n";
        String contentString = "content = " + this.content + " \n";
        String imageString = "";
        for(String imageValue : imageList) {
            imageString += "图片:" + imageValue + " \n";
        }
        String end = "------------end-------------\n";
        Log.e("DocumentL:", diver + contentString + imageString + end);
        return diver + contentString + imageString + end;
    }
}

在原型Document中,我们实现了Cloneable接口,在clone方法内,拷贝了Document对象

        Document  document = new Document();
        document.setContent("我是内容");
        document.addImage("我是图片1");
        document.addImage("我是图片2");
        document.toString();

        //clone
        Document documentClone = document.clone();
        documentClone.setContent("我是修改后的内容");
        documentClone.addImage("我是图片3");
        documentClone.toString();

        //再次打印原版
        document.toString();

在这里插入图片描述
拷贝的Document对象修改了文本内容,但是原型对象文本内容并没有被修改。 但是呢,细心的同学可能发现了,拷贝的对象添加了一张图片,而原型对象中的图片也随之被修改了(添加了)。这是为什么?

深拷贝和浅拷贝

出现上述问题的原因是因为是属于:浅拷贝。

关于深拷贝和浅拷贝,用C++的地址内存来说的话就是:不管是基本数据类型还是引用数据类型,都会在栈中分配一块内存,基本数据类型在栈中的那块内存存储的直接是它的值,而引用数据类型在栈中存储的是一个地址(相当于一个指针),而这个地址指向了堆中的真正的值。也就是说:引用数据类型真正的值是存放在堆内存中的。

浅拷贝:只用把自己赋值给新对象,而不管自己是什么类型(引用数据类型还是基本数据类型);
深拷贝:如果是引用数据类型,就调用起自己的clone方法拷贝,如果是基本数据类型就直接赋值。

还记得我们最早学习Java的时候的:值传递和引用传递吗?对,就是这个东西。我们的文本内容是:基本数据类型,而图片对象是:引用数据类型。此时我们的引用数据类型就相当于是浅拷贝,也就是说:

//直接复制的是值
documentCopy.content = this.content;
//直接传递的是数组的引用
documentCopy.imageList = this.imageList;

知道原因后,我们修改clone方法如下:

    @Override
    protected Document clone() {
        Document documentCopy = null;
        try{
            documentCopy = (Document) super.clone();
            ArrayList<String> imageListClone = (ArrayList<String>) this.imageList.clone();
            documentCopy.content = this.content;
            documentCopy.imageList = imageListClone;
        } catch (CloneNotSupportedException e) {

        }
        return documentCopy;
    }

在这里插入图片描述
由于ArrayList类本身实现了Cloneable接口,所以可以直接调用其clone方法复制对象。此时打印结果正确。

原型模式的其它实现方式

Intent的使用相信是非常熟悉的,看如下代码:

Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:123456"));
intent.putExtra("sms_body", "Hello world!");
Intent cloneIntent = (Intent) intent.clone();
startActivity(cloneIntent);
上述代码是发送短信。clone了Intent对象,我们看下clone方法的源码:
    @Override
    public Object clone() {
        return new Intent(this);
    }
   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的方法内,直接创建了Intent对象。而没有使用拷贝的方法创建,因此我们可以知道,在实际项目中,我们要根据自身情况看是否要:创建对象。

源码示例地址:
GitHub示例源码
GitHub示例源码
GitHub示例源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值