设计模式(Java语言)- 原型模式
原型模式(Prototype Pattern)也有人将原型模式称为克隆模式,是属于创造型设计模式,用于创建重复的对象,提供了一种创建对象的最佳方式。原型模式需要实现Cloneable接口,来实现对象的克隆。在实际的应用中,如果应用需要反复创建相同的对象时,并且创建这个对象需要花费大量时间或者需要访问权限,比如需要读取数据库,配置文件等,如果每次创建重复对象都需要读一次数据库,那么这种方式显然并不是高效的。这时可以考虑使用原型模式来解决,提高效率,此时只需要在创建原型对象时需要读取一次数据库或配置文件等,当后面需要需要创建这个对象时只需要从原型对象克隆一个出来即可。另外,原型模式也解决了构建复杂对象时繁琐的过程,原型模式不关心对象创建的细节,用户只需要调用克隆的方法就可以创建出一个一摸一样的对象,简化创建流程。
既然原型模式也成为克隆模式,那么对象复制过程必然用到Java的克隆方法。所以你也需要了解什么是浅克隆和深克隆。
浅克隆
浅克隆复制的是对象基本类型的属性,对于引用类型的属性,浅克隆置复制该应用类型的地址,因为克隆对象的被克隆对象的应用类型属性是同一个内存地址,即为同一个对象,所以在修改其中一个对象的该属性时,另一个对象的改属性也会被修改,很容易将原型对象属性修改,这也是在使用原型模式时需要注意的地方。浅克隆在代码中的实现也比较简单,Java语言中本身就已经提供相关的接口和方法了,我们在使用时只需要继承Cloneable接口,重写clone方法即可实现对象的浅克隆。代码实现如下:
public class Sheep implements Cloneable {
private String name;
private Color color = new Color();
public Sheep() {
}
public Sheep(String name, String color) {
this.name = name;
setColor(color);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", color=" + color +
'}';
}
public Color getColor() {
return color;
}
public void setColor(String color) {
this.color.setColor(color);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Color implements Cloneable {
private String color;
public Color() {
}
public Color(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Color{" +
"color='" + color + '\'' +
'}';
}
}
测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep test = new Sheep("test","白色");
System.out.println(test);
Sheep clone = (Sheep) test.clone();
clone.setColor("黑色");
clone.setName("test01");
System.out.println(test);
System.out.println(clone);
}
}
运行程序时控制台打印出了:
Sheep{name='test', color=Color{color='白色'}}
Sheep{name='test', color=Color{color='黑色'}}
Sheep{name='test01', color=Color{color='黑色'}}
很显然,test对象创建时是白色的,然后用这个对象进行克隆得到 clone 实例,然后将clone 对象的颜色修改成黑色,name修改成test01,最终两个对象的颜色都变成了黑色,印证了上面说的话,对于引用类型克隆的是对象的内存地址。可能会有人好奇,String也是引用类型,为什么克隆对象修改了name属性,原型对象却没有被修改了?这是因为String是final类型,克隆过程中自然会是两个不同的内存地址。
深克隆
深克隆和浅克隆的区别在于,深克隆时引用类型属性复制的是该属性的值,与原型对象的拥有不同的内存地址,即两个是不同的对象,他们任意一个改属性值都不会影响到彼此。深克隆的实现方式有两种,第一种,实现Cloneable接口,重写clone方法,与浅克隆不同的是多一步将引用类型的变量再调用一次改变量的clone方法。不推荐用这种方法实现深克隆,每次修改对象的变量时都需要修改一次clone方法,违反了ocp原则。第二种,利用Java序列化与发序列化来实现,推荐使用这种方式。
代码实现:
public class Color implements Cloneable, Serializable {
private String color;
public Color() {
}
public Color(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Color{" +
"color='" + color + '\'' +
'}';
}
}
public class Sheep implements Cloneable, Serializable {
private String name;
private Color color = new Color();
public Sheep() {
}
public Sheep(String name, String color) {
this.name = name;
setColor(color);
}
/**
* 利用序列化与反序列化实现深克隆
* @return
*/
public Object deepClone() {
ByteArrayInputStream bis = null;
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return ois.readObject();
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (ois != null) {
ois.close();
}
if (bis != null) {
bis.close();
}
if (oos != null) {
oos.close();
}
if (bos != null) {
bos.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep clone = (Sheep) super.clone();
clone.color = (Color) clone.color.clone();
return clone;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", color=" + color +
'}';
}
public Color getColor() {
return color;
}
public void setColor(String color) {
this.color.setColor(color);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
注意,如果使用Java的序列化与反序列化,则改对象需要实现Serializable接口,否则会抛序列化异常。
总结
1、原型模式有两种实现方式,第一种利用Object类中的clone方式,重写Cloneable的clone方法,浅克隆时直接调用Object类提供的clone方式即可。深克隆则需要再调用需要被克隆的对象的clone方法,当然该对象也必须实现Cloneable接口。第二种方式是利用Java的序列化和反序列化技术,这种方式也有一个缺点是所有需要序列化的变量都必须要实现Serializable接口。
2、原型模式的优点:提高效率;屏蔽复杂的对象构建过程,简化代码。
3、原型模式的缺点:
1)配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2)必须实现 Cloneable 接口或Serializable接口。
4、原型模式的应用场景:
1)资源优化场景。
2)对象初始化需要大量的资源,包括数据,硬件资源等。
设计模式(Java语言)- 原型模式的更多相关文章
- java23种设计模式——四、原型模式
源码在我的github和gitee中获取 目录 java23种设计模式-- 一.设计模式介绍 java23种设计模式-- 二.单例模式 java23种设计模式--三.工厂模式 java23种设计模式- ...
- Java设计模式5:原型模式
原型模式 原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意. 原型模式结构 原型模式要求对象实现一个 ...
- java 之 原型模式(大话设计模式)
原型模式,在笔者理解看来就是克隆,当我们在创建第一个对象时,已经给对象赋值完毕,此时我们需要一个当前对象的副本,如果没有原型模式,我们会再次创建一个对象,然后后二次赋值,保证两个对象完全一致, 这样我 ...
- Java描述设计模式(05):原型模式
本文源码:GitHub·点这里 || GitEE·点这里 一.原型模式简介 1.基础概念 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出 ...
- 《JAVA设计模式》之原型模式(Prototype)
在阎宏博士的<JAVA与模式>一书中开头是这样描述原型(Prototype)模式的: 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办 ...
- 重学 Java 设计模式:实战原型模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 老板你加钱我的代码能飞 程序员这份工作里有两种人:一类是热爱喜欢的.一类是仅当成工作 ...
- 深度分析:java设计模式中的原型模式,看完就没有说不懂的
前言 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的 ...
- java常用设计模式三:原型模式
在说原型模式之前先说一下浅拷贝和深拷贝的概念 一.浅拷贝和深拷贝 1.浅拷贝 在java中,对象创建后需要有一个引用变量来指向该对象实际的地址空间,也就是说引用变量与对象实体是两个不同的数据体.在Ob ...
- java设计模式-----5、原型模式
原型(Prototype)模式是一种对象创建型模式,他采取复制原型对象的方法来创建对象的实例.使用原型模式创建的实例,具有与原型一样的数据. 原型模式的特点: 1.由原型对象自身创建目标对象.也就是说 ...
- 【java设计模式】-06原型模式
原型模式简述 定义: 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象 ,也就是通过复制现有对象实例产生新的对象,也就是所谓的"克隆" 实现方式: 1.实现Cl ...
随机推荐
- 在MAC上如何使用SQL Server
由于小编在这学期要学习数据库原理这门课程,需要用到SQL Server,然而大家都知道SQL Server目前是只能在Windows上使用,我们在mac电脑上如何使用呢?我们可以借助目前比较火的Doc ...
- matplotlib BboxBase类
2020-04-07 17:24:12 --Edit by yangray BboxBase 是 TransformNode 的子类, 同时它是所有 bounding box(平行四边形限位框) 的 ...
- python3(二十二) oop
""" 面向对象编程 """ __author__ = 'shaozhiqi' # 面向对象的程序设计把计算机程序视为一组对象的集合,而每个 ...
- mysql截取函数常用方法 即mysql 字符串 截取-- - 最后带上java字符串截取规则比较
常用的mysql截取函数有:left(), right(), substring(), substring_index() 下面来一一说明一下: 1.左截取left(str, length) 说明:l ...
- 【Java】抽象类、接口
什么是抽象类? 特点: - 抽象类几乎普通类一样,除了不能实例化 - 不能实例化不代表没有构造器,依然可以声明构造器,便于子类实例化调用 - 具有抽象方法的类,一定是抽象类 abstract 抽象的 ...
- 基于 HTML5 WebGL 的 CPU 监控系统
前言 科技改变生活,科技的发展带来了生活方式的巨大改变.随着通信技术的不断演进,5G 技术应运而生,随时随地万物互联的时代已经来临.5G 技术不仅带来了更快的连接速度和前所未有的用户体验,也为制造业, ...
- 这届网友实在是太有才了!用python爬取15万条《我是余欢水》弹幕
年初时我们用数据解读了几部热度高,但评分差强人意的国产剧,而最近正午阳光带着两部新剧来了,<我是余欢水>和<清平乐>,截止到目前为止,这两部剧在豆瓣分别为7.5分和7.9分,算 ...
- M - Help Hanzo LightOJ - 1197 (大区间素数筛法)
题解:素数区间问题.注意到a和b的范围是1<<31,所以直接暴力打表肯定不可以.如果一个数是合数,他的两个因子要么是两个sqrt(x),要么就分布在sqrt(x)两端,所以我们可以根据sq ...
- 如何将你的 Vue.js 项目部署在云开发静态托管之上
云开发静态托管是云开发提供的静态网站托管的能力,静态资源(HTML.CSS.JavaScript.字体等)的分发由腾讯云对象存储 COS 和拥有多个边缘网点的腾讯云 CDN 提供支持. 在云开发静态托 ...
- jmeter5.1.1 生成html报告
1.首先需要准备好 .jmx 脚本 2.修改jmeter.properties文件(把注解去掉,报告中才能展示所需信息) jmeter.save.saveservice.output_format=x ...