设计模式(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 ...
随机推荐
- DataAnalysis-Pandas分组聚合
title: Pandas分组聚合 tags: 数据分析 python categories: DataAnalysis toc: true date: 2020-02-10 16:28:49 Des ...
- 30.6 HashMap的使用
/* * * 使用HashMap存储数据并遍历(字符串作为key) * *使用HashMap存储数据并遍历(自定义对象作为key) */ 字符串做key和Map的使用一样,重点介绍自定义对象作为key ...
- zookeeper的下载安装和选举机制(zookeeper一)
1. 简要概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的框架.Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架它负责存储和管理大家都关心的 ...
- golang trace 分析 简例
今天,通过一个例子,一方面熟悉trace在自定义范围内的分析,另一方面golang 在协程调度策略上的浅析. Show Code // trace_example.go package main im ...
- AJ学IOS(53)多线程网络之NSOperation简介
AJ分享,必须精品 一:简单介绍 1:NSOperation的作⽤使用步骤: 配合使用NSOperation和NSOperationQueue也能实现多线程编程. NSOperation和NSOper ...
- 从前端到后端实现弹幕的过程(jsp/Jquery.barrager.js)
Jquery.barrager.js插件,可以去网上下载!下载完后,就把下载文件中的js文件.css文件.图片文件.等等等文件全部拷贝到你们自己的项目中去,千万别拷贝漏了,如果你怕拷贝漏了什么,那就把 ...
- 如何让一张图片变成二值图像?python+opencv图像处理
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:张熹熹 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- 常见DL网络模型参数
- stand up meeting 11/30/2015
part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 完善了UI的各项功能,弹窗的显示格式等方面的规范:解决logic部分调用该dll的问题:解决鼠标事件的捕捉中~ ...
- Linux学习笔记(九)Vim文本编辑器的使用
Vim文本编辑器的使用 Vim的工作模式 1.命令模式 2.输入模式 3.编辑模式 进入Vim 1.使用Vim打开文件 2.直接进入指定位置 Vim基本命令 1.插入命令 2.光标移动命令 3.使用V ...