Android设计模式之——原型模式

一、介绍

原型模式是一个创建型的模式。原型二字表明了该模型应该有一个样板实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程也就是我们俗称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型也是可定制的。原型模型多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。

二、定义

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

三、使用场景

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

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

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

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

四、原型模型的UML类图

图1

图中角色介绍:

  • Client:客户端用户。

  • Prototype:抽象类或者接口,声明具备clone能力。

  • ConcretePrototype:具体的原型类。

五、原型模式的简单实现

下面以简单的文档拷贝为例来演示一下简单的原型模式,我们在这个例子中首先创建了一个文档对象,即WordDocument,这个文档中含有文字和图片。用户经过了长时间的内容编辑后,打算对该文档做进一步的编辑,但是,这个编辑后的文档是否会被采用还不确定,因此,为了安全起见,用户需要将当前文档拷贝一份,然后再在文档副本上进行修改,这与《Effective Java》一书中提到的保护性拷贝有些类似,如此,这个原始文档就是我们上述所说的样板实例,也就是将要被“克隆”的对象,我们成为原型:

示例代码:

/*** 文档类型,扮演的是ConcretePrototype角色,而cloneable是代表prototype角色*/
public class WordDocument implements Cloneable {//文本private String mText;//图片名列表private ArrayList<String> mImages = new ArrayList<String>();public WordDocument(){System.out.println("-------- WordDocument构造函数 --------");}public String getText(){return this.mText;}public void setText(String text){this.mText = text;}public ArrayList<String> getImages(){return this.mImages;}public void setImages(ArrayList<String> images){this.mImages = images;}public void addImage(String img){this.mImages.add(img);}/*** 打印文档*/public void showDocument(){System.out.println("-------- Word Content Start --------");System.out.println("Text : " + this.mText);System.out.println("Images List : ");for(String image : mImages){System.out.println("image name : " + image);}System.out.println("-------- Word Content End --------");}@Overrideprotected WordDocument clone(){try{WordDocument doc = (WordDocument)super.clone();doc.mText = this.mText;doc.mImages = this.mImages;return doc;}catch(Exception e){}return null;}
}

执行方法:

    public static void main(String[] args) throws IOException {//1.构建文档对象WordDocument originDoc = new WordDocument();//2.编辑文档,添加图片等originDoc.setText("这是一篇文档");originDoc.addImage("图片一");originDoc.addImage("图片二");originDoc.addImage("图片三");originDoc.showDocument();//以原始文档为原型,拷贝一份副本WordDocument doc2 = originDoc.clone();doc2.showDocument();//修改文档副本doc2.setText("这是修改过的Doc2文本");doc2.addImage("这是新添加的图片");originDoc.showDocument();doc2.showDocument();}

执行结果:

-------- WordDocument构造函数 --------
//originDoc
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
-------- Word Content End --------//doc2
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
-------- Word Content End --------//副本修改后originDoc
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
image name : 这是新添加的图片
-------- Word Content End --------//副本修改后doc2
-------- Word Content Start --------
Text : 这是修改过的Doc2文本
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
image name : 这是新添加的图片
-------- Word Content End --------

这里我们发现通过修改doc2后,只是影响了originDoc的mImages,而没有改变mText。

六、浅拷贝和深拷贝

上述原型模式的实现实际上只是一个浅拷贝,也称影子拷贝,这份拷贝实际上并不是将原始的文档的所有字段都重新构造了一份,而是副本文档的字段引用原始文档的字段,如下图:

这里写图片描述

细心的读者可能从上面的结果中发现,最后两个文档信息输出是一致的。我们在doc2添加了一张图片,但是,同时也显示在originDoc中,这是怎么回事呢?学习过C++的读者都会有比较深刻的体会,这是因为上文中WordDocument的clone方法中只是简单的进行了浅拷贝,引用类型的新对象doc2.mImages只是单纯的指向了this.mImages引用,并没有重新构造一个mImages对象,然后将原始文档中的图片添加到新的mImages对象中,这样就导致doc2.mImages与原始文档中的是同一个对象,因此,修改了其中一个文档中的图片,另一个文档也会受影响。那么如何解决这个问题呢?答案就是采用深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯引用的形式。

clone方法修改如下(其他不变):

    @Overrideprotected WordDocument clone(){try{WordDocument doc = (WordDocument)super.clone();doc.mText = this.mText;//对mImages对象也调用clone()函数,进行深拷贝doc.mImages = (ArrayList<String>)this.mImages.clone();return doc;}catch(Exception e){}return null;}

修改后在执行上述代码的结果是:

-------- WordDocument构造函数 --------
//originDoc
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
-------- Word Content End --------//doc2
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
-------- Word Content End --------//副本修改后originDoc
-------- Word Content Start --------
Text : 这是一篇文档
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
-------- Word Content End --------//副本修改后doc2
-------- Word Content Start --------
Text : 这是修改过的Doc2文本
Images List : 
image name : 图片一
image name : 图片二
image name : 图片三
image name : 这是新添加的图片
-------- Word Content End --------

可以看出现在互不影响,这个叫做深拷贝。

接着上面的疑问,其实String类型在浅拷贝时和引用类型一样,没有单独复制,而是引用同一地址,因为String没有实现cloneable接口,也就是说只能复制引用。(这里我们可以查看源码可以看到,而ArrayList实现了cloneable接口)但是当修改其中的一个值的时候,会新分配一块内存用来保存新的值,这个引用指向新的内存空间,原来的String因为还存在指向他的引用,所以不会被回收,这样,虽然是复制的引用,但是修改值的时候,并没有改变被复制对象的值。

所以在很多情况下,我们可以把String在clone的时候和基本类型做相同的处理,只是在equals时注意一些就行了。

原型模式是非常简单的一个模式,它的核心问题就是对原始对象进行拷贝,在这个模式的使用过程中需要注意的一点就是:深、浅拷贝的问题。在开发过程中,为了减少错误,作者建议使用该模式时尽量使用深拷贝,避免操作副本时影响原始对象的问题。

七、Android源码中的原型模式

示例代码:

    Uri uri = Uri.parse("smsto:110");Intent intent = new Intent(Intent.ACTION_SEND,uri);intent.putExtra("sms_body", "The SMS text");//克隆Intent intent2 = (Intent)intent.clone();startActivity(intent2);

八、总结

原型模式本质上就是对象的拷贝,与C++中的拷贝构造函数有些类似,它们之间容易出现的问题也都是深拷贝、浅拷贝。使用原型模式可以解决构建复杂对象的资源消耗问题,能够在某些场景下提升创建对象的效率。

优点:

  • (1)原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可能更好的体现其优点。

  • (2)还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。

缺点:

  • (1)这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

  • (2)通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

针对C++异常的学习

源码 头文件 sdf_exception.h #pragma once#include <exception> #include <string>namespace sdf {namespace common{using sdf_error_code_t uint32_t;class SdfException : std::exception{public:explicit SdfException(sdf_error_code_t errorCode) : erro…

Android设计模式之——抽象工厂模式

一、介绍 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;&#xff0c;也是创建型设计模式之一。前一节我们已经了解了工厂方法模式&#xff0c;那么这个抽象工厂又是怎么一回事呢&#xff1f;大家联想一下现实生活中的工厂肯定都是具体的&#xff0c;也就是说…

Android设计模式之——策略模式

一、介绍 在软件开发中也常常遇到这样的情况&#xff1a;实现某一个功能可以有多种算法或者策略&#xff0c;我们根据实际情况选择不同的算法或者策略来完成该功能。例如&#xff0c;排序算法&#xff0c;可以使用插入排序、归并排序、冒泡排序等。 针对这种情况&#xff0c;…

密码学在区块链隐私保护中的应用学习

身份隐私保护技术 混淆服务 混淆服务的目的在于混淆消息双方的联系&#xff08;如 图 2 所示&#xff09;。当发送方需要告知接收方消息 M 时&#xff0c; 它会首先用接收方的公钥 KB 加密 M&#xff0c;并在密文后 附带真实接收地址 R。为了借助第三方&#xff08;图 2 中的…

值类型和引用类型的区别

一、定义 引用类型表示你操作的数据是同一个&#xff0c;也就是说当你传一个参数给另一个方法时&#xff0c;你在另一个方法中改变这个变量的值&#xff0c;那么调用这个方法是传入的变量的值也将改变。 值类型表示复制一个当前变量传给方法&#xff0c;当你在这个方法中改变…

面向区块链的高效物化视图维护和可信查询论文学习

物化视图介绍 如何维护物化视图仍旧是一个开放问题.在关系数据库中,增量刷新的物化视图维护策略可划分为立即维护和延迟维护两大类.立即维护策略的优点是实现较为简单,在单数据源下不 存在一致性问题;然而该策略将物化视图维护过程嵌入到更新事务之中,延长了更新事务的提交时间…

密码学数字信封的介绍

对称密码和非对称密码 对称密码&#xff1a;加解密运算非常快&#xff0c;适合处理大批量数据&#xff0c;但其密码的分发与管理比较复杂非对称密码&#xff1a;公钥和私钥分离&#xff0c;非常适合密钥的分发和管理 数字信封的定义 如果将对称密码算法和非对称密码算法的优点…

Android设计模式之——状态模式

一、介绍 状态模式中的行为是由状态来决定的&#xff0c;不同的状态下有不同的行为。状态模式和策略模式的结构几乎完全一样&#xff0c;但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的&#xff0c;策略模式的行为是彼此独立、可相互替换的。用一句话来…

Android设计模式之——责任链模式

一、介绍 责任链模式&#xff08;Iterator Pattern&#xff09;&#xff0c;是行为型设计模式之一。什么是”链“&#xff1f;我们将多个节点首尾相连所构成的模型称为链&#xff0c;比如生活中常见的锁链&#xff0c;就是由一个个圆角长方形的铁环串起来的结构。对于链式结构…

目前基于区块链的档案防篡改系统的设计如何实现防篡改

架构设计图 分析 为了保障档案数据的安全性和隐私性&#xff0c;存储档案附件和档案属性存储加密存储在私有IPFS集群&#xff0c;档案的IPFS地址和数字指纹存储在私有区块链上。公有区块链定期存储和检查私有区块链最新不可逆区块的高度和哈希值&#xff0c;以保障私有区块链上…

IPFS的文件存储模式

IPFS是如何进行文件存储的 IPFS采用的索引结构是DHT&#xff08;分布式哈希表&#xff09;&#xff0c;数据结构是MerkleDAG&#xff08;Merkle有向无环图&#xff09; DHT(分布式哈希表) 参考链接MerkleDAG&#xff08;Merkle有向无环图&#xff09; 参考链接MerkleDAG功能…

Android设计模式之——解释器模式

一、介绍 解释器模式&#xff08;Interpreter Pattern&#xff09;是一种用的比较少的行为型模式&#xff0c;其提供了一种解释语言的语法或表达式的方式&#xff0c;该模式定义了一个表达式接口&#xff0c;通过该接口解释一个特定的上下文。在这么多的设计模式中&#xff0c…

在Docker里面安装Ubuntu,并且使用ssh进行连接

创建Ubuntu镜像 1&#xff0c;拉取Ubuntu系统的镜像 docker pull ubuntu2、查看拉取是否成功 docker images3&#xff0c;运行容器 docker run --name 新建的容器的名字 -ti -v /AAA:/BBB -d -p 3316:22 ubuntu(这个是镜像的名字)宿主机根目录中的AAA文件夹就映射到了容器…

Android设计模式之——命令模式

一、介绍 命令模式&#xff08;Command Pattern&#xff09;&#xff0c;是行为型设计模式之一。命令模式相对于其他的设计模式来说并没有那么多的条条框框&#xff0c;其实它不是一个很”规范“的模式&#xff0c;不过&#xff0c;就是基于这一点&#xff0c;命令模式相对于其…

Android设计模式之——观察者模式

一、介绍 观察者模式是一个使用率非常高的模式&#xff0c;它最常用的地方是GUI系统、订阅——发布系统。因为这个模式的一个重要作用就是解耦&#xff0c;将被观察者和观察者解耦&#xff0c;使得它们之间的依赖性更小&#xff0c;甚至做到毫无依赖。以GUI系统来说&#xff0…

Android设计模式之——备忘录模式

一、介绍 备忘录模式是一种行为模式&#xff0c;该模式用于保存对象当前状态&#xff0c;并且在之后可以再次恢复到此状态&#xff0c;这有点像我们平时说的”后悔药“。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问&#xff0c;目的是为了保护好被保存…

C++ using的三种使用策略以及具体的用法

Using的使用方法 1&#xff0c;命名空间的使用 为了防止代码冲突&#xff0c;都会使用到命名空间。假设这样一种情况&#xff0c;当一个班上有两个名叫 Zara 的学生时&#xff0c;为了明确区分他们&#xff0c;我们在使用名字之外&#xff0c;不得不使用一些额外的信息&#…

Android设计模式之——迭代器模式

一、介绍 迭代器模式&#xff08;Iterator Pattern&#xff09;又称为游标&#xff08;Cursor&#xff09;模式&#xff0c;是行为型设计模式之一。迭代器模式算是一个比较古老的设计模式&#xff0c;其源于对容器的访问&#xff0c;比如Java中的List、Map、数组等&#xff0c…

Android设计模式之——模板方法模式

一、介绍 在面向对象开发过程中&#xff0c;通常会遇到这样的一个问题&#xff0c;我们知道一个算法所需的关键步骤&#xff0c;并确定了这些步骤的执行顺序&#xff0c;但是&#xff0c;某些步骤的具体实现是未知的&#xff0c;或者说某些步骤的实现是会随着环境的变化而改变…

Android设计模式之——访问者模式

一、介绍 访问者模式是一种将数据操作与数据结构分离的设计模式&#xff0c;它是《设计模式》中23种设计模式中最复杂的一个&#xff0c;但它的使用频率并不高&#xff0c;正如《设计模式》的作者GOF对访问者模式的描述&#xff1a;大多数情况下&#xff0c;你不需要使用访问者…