Java 设计模式系列(五)原型模式
设计模式之美 - 原型模式
设计模式之美目录:https://www.cnblogs.com/binarylei/p/8999236.html
原型模式:如果对象的创建成本比较大,可以基于已有的原型对象来创建新的对象。
对于熟悉 JavaScript 语言的前端程序员来说,原型模式是一种比较常用的开发模式。这是因为,有别于 Java、C++ 等基于类的面向对象编程语言,JavaScript 是一种基于原型的面向对象编程语言。即便 JavaScript 现在也引入了类的概念,但它也只是基于原型的语法糖而已。
1. 原型模式的结构
原型模式怎么实现的,其实只要想一想 Java Object#clone 方法就清楚了。原型模式有两种表现形式: 简单形式和登记形式。
1.1 简单原型模式

这种形式涉及到三个角色:
(1)客户(Client)角色:客户类提出创建对象的请求。
(2)抽象原型(Prototype)角色:这是一个抽象角色,通常由一个 Java 接口或 Java 抽象类实现。此角色给出所有的具体原型类所需的接口。
(3)具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
1.2 登记式原型模式

登记式原型模式相对于简单原型模式,多了一个原型管理器(PrototypeManager)角色,该角色的作用是:创建具体原型类的对象,并记录每一个被创建的对象。
1.3 简单原型 vs 登记式原型
简单原型和登记式原型模式各有其长处和短处。
- 简单原型:如果需要创建的原型对象数目较少而且比较固定的话,可以采取这种原型模式。在这种情况下,原型对象的引用可以由客户端自己保存。
- 登记式原型:如果要创建的原型对象数目不固定的话,可以采取登记式。此时,先从缓存中命中原型对象,如果没有则调用其 clone 方法创建对象。这是不是和我们常用的数据库连接池、线程池很像了呢?
1.4 原型模式 vs 单例模式
- 单例对象允许有限的多个:如数据库连接池、线程池等,是不是就变化成原型模式呢?
- 每种类型的单例对象只允许一个:如 Logger 日志,每个 class.getName 都创建一个实例,这其实也是单例模式的变种。
2. Java 中的克隆方法
Object 类提供 clone() 方法对对象进行复制,子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法。对象的复制有一个基本问题,就是对象通常都有对其他的对象的引用。也就是说:Object#clone 方法是浅拷贝。
Java 语言提供的 Cloneable 接口只起一个作用,就是在运行时期通知 Java 虚拟机可以安全地在这个类上使用 clone() 方法。通过调用这个 clone() 方法可以得到一个对象的复制。由于 Object 类本身并不实现 Cloneable 接口,因此如果所考虑的类没有实现 Cloneable 接口时,调用 clone() 方法会抛出 CloneNotSupportedException 异常。
2.1 克隆满足的条件
clone() 方法将对象复制了一份并返还给调用者。所谓“复制”的含义与 clone() 方法是怎么实现的。一般而言,clone() 方法满足以下的描述:
(1)对任何的对象 x,都有:x.clone() != x。换言之,克隆对象与原对象不是同一个对象。
(2)对任何的对象 x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原对象的类型一样。
(3)如果对象 x 的 equals() 方法定义其恰当的话,那么 x.clone().equals(x) 应当成立的。
在 JAVA 语言的 API 中,凡是提供了 clone() 方法的类,都满足上面的这些条件。JAVA 语言的设计师在设计自己的 clone() 方法时,也应当遵守着三个条件。一般来说,上面的三个条件中的前两个是必需的,而第三个是可选的。
2.2 浅克隆和深克隆
无论你是自己实现克隆方法,还是采用 Java 提供的克隆方法,都存在一个浅度克隆和深度克隆的问题。
浅度克隆:只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。
深度克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。
2.3 利用序列化实现深度克隆
把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。在 Java 语言里深度克隆一个对象,可以先使对象实现 Serializable 接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。
public Object deepClone() throws IOException, ClassNotFoundException{
//将对象写到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流里读回来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
说明: 这样做的前提就是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成 transient,从而将之排除在复制过程之外。有一些对象,比如 Thread 或 Socket 对象,是不能简单复制或共享的。不管是使用浅度克隆还是深度克隆,只要涉及这样的间接对象,就必须把间接对象设成 transient 而不予复制。
3. 什么时候使用原型模式
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大,在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。那何为 "对象的创建成本比较大"?
实际上,创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。这种情况,不建议使用原型模式。
如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。此时,建议使用原型模式,如数据库连接池、线程池等各种资源池。
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java 设计模式系列(五)原型模式的更多相关文章
- Java设计模式系列-装饰器模式
原创文章,转载请标注出处:<Java设计模式系列-装饰器模式> 一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是 ...
- Java设计模式系列-抽象工厂模式
原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755412.html 一.概述 抽象工厂模式是对工厂方法模式的再升级,但是二者面对的场景稍显差别. ...
- Java设计模式系列-工厂方法模式
原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...
- 《JAVA设计模式》之原型模式(Prototype)
在阎宏博士的<JAVA与模式>一书中开头是这样描述原型(Prototype)模式的: 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办 ...
- 重学 Java 设计模式:实战原型模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 老板你加钱我的代码能飞 程序员这份工作里有两种人:一类是热爱喜欢的.一类是仅当成工作 ...
- 设计模式系列之原型模式(Prototype Pattern)——对象的克隆
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- Java设计模式5:原型模式
原型模式 原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意. 原型模式结构 原型模式要求对象实现一个 ...
- 深度分析:java设计模式中的原型模式,看完就没有说不懂的
前言 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的 ...
- Java设计模式系列1--原型模式(Prototype Method)
2014-02-14 11:27:33 声明:本文不仅是本人自己的成果,有些东西取自网上各位大神的思想,虽不能一一列出,但在此一并感谢! 原型模式,从名字即可看出,该模式的思想就是将一个对象作为原型, ...
- java设计模式-----5、原型模式
原型(Prototype)模式是一种对象创建型模式,他采取复制原型对象的方法来创建对象的实例.使用原型模式创建的实例,具有与原型一样的数据. 原型模式的特点: 1.由原型对象自身创建目标对象.也就是说 ...
随机推荐
- 51Nod 1049:最大子段和(dp)
1049 最大子段和 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 N个整数组成的序列a[1],a[2],a[3],-,a[n],求该序列如a[i]+ ...
- 6-3 Add Two Polynomials(20 分)
Write a function to add two polynomials. Do not destroy the input. Use a linked list implementation ...
- 《FDTD electromagnetic field using MATLAB 》读书笔记001-差商种类
有限差分就是用差商代替微商,有3钟: 1.向前差商 2.向后差商 3.中心差商 上面三张途中虚线就是函数在x的精确微商(偏导数),直线就是用来代替精确 微商的差商格式.
- Linux内核配置---menuconfig
1. 示例 config SGI_NEWPORT_CONSOLE tristate "SGI Newport Console support" depends on SGI_IP2 ...
- printk()函数学习笔记
参考: https://www.cnblogs.com/sky-heaven/p/6742062.html韦东山老师的printk讲解:https://blog.csdn.net/W110710131 ...
- ballerina 学习二十五 项目docker 部署&& 运行
ballerina 官方提供了docker 的runtime,还是比较方便的 基本项目创建 使用cli创建项目 按照提示操作就行 ballerina init -i 项目结构 添加了dockerfil ...
- FastAdmin Bootstrap-table 特定某行背景变红
FastAdmin Bootstrap-table 特定某行背景变红 rowStyle: function (row, index) { var style = {css:{'background': ...
- django 模板关闭特殊字符转化
默认情况下,在django admin管理界面和页面中,如果输出的对象中包含HTML特殊字符,在django中默认的处理方式是将对象中的HTML特殊字符转化,例如会将 "<" ...
- Call to your teacher
链接:https://www.nowcoder.net/acm/contest/76/F来源:牛客网 Call to your teacher 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/ ...
- HTTP协议响应头之Transfer-Encoding:分块传输详解
Http Connection有两种连接方式:短连接和长连接:短连接即一次请求对应一次TCP连接的建立和销毁过程,而长连接是多个请求共用同一个连接这样可以节省大量连接建立时间提高通信效率.目前主流浏览 ...