Java设计模式:Prototype(原型)模式
概念定义
使用原型实例指定待创建对象的种类,并通过拷贝该原型来创建新的对象。Prototype模式允许一个原型对象克隆(复制)出多个与其相同的对象,而无需知道任何如何创建的细节。
应用场景
- 对象的创建过程较为复杂且需要频繁创建
- 期望根据现有的实例来生成新的实例,例如:
- 对象种类繁多而无法整合到一个类时
- 难以通过指定类名生成实例时
- 希望解耦框架与生成的实例时
在实际应用中,Prototype模式很少单独出现。经常与其他模式混用。
原型实现
所有Java类都继承自java.lang.Object,而Object类提供clone()方法来克隆对象。因此,Java类实现Cloneable接口并重写clone()方法后,即可实现Prototype模式。
实现Prototype模式的示例代码如下:
// 原型类(也可定义为interface Prototype extends Cloneable)
abstract class Prototype implements Cloneable {
private String name;
public void setName(String name) {this.name = name;}
public String getName() {return this.name;}
public abstract void doSomething();
@Override
public Object clone() { // clone()方法用public修饰
Object object = null;
try {
object = super.clone(); // 调用父类clone()方法
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return object;
}
}
// 具体实现类(继承自Prototype类自动具有克隆功能)
class ConcretePrototype extends Prototype{
public ConcretePrototype() { setName("ConcretePrototype"); }
@Override
public void doSomething() { System.out.println("I'll clone myself!"); }
}
public class PrototypeDemo {
public static void main(String[] args) {
ConcretePrototype cp = new ConcretePrototype();
cp.doSomething();
for(int i=0; i< 5; i++){
ConcretePrototype cloned = (ConcretePrototype)cp.clone();
System.out.println("I'm cloned by " + cloned.getName());
}
}
}
执行后输出如下:
I'll clone myself!
I'm cloned by ConcretePrototype
I'm cloned by ConcretePrototype
I'm cloned by ConcretePrototype
I'm cloned by ConcretePrototype
I'm cloned by ConcretePrototype
模式优缺点
Prototype模式的优点如下:
- 根据客户端要求实现运行时动态创建对象,客户端不需要知道对象的创建细节,便于代码的维护和扩展。
- 当对象的创建较为复杂或重复创建大量相似对象时,可简化对象创建,且性能比直接new对象更高(new会自动调用构造链中的所有构造方法,但clone不会调用任何类构造方法)。
- Prototype模式类似工厂模式,但没有工厂模式中的抽象工厂和具体工厂的层级关系,代码结构更清晰和简单。
缺点如下:
- 需要为每个类实现Cloneable接口并重写clone()方法,改造已有类时必须修改其源码,违背"开闭原则"。
- 单例模式与工厂模式、Prototype模式是冲突的,尽量不要混用。
- 在实现深拷贝(深克隆)时需要编写较为复杂的代码。
注意事项
clone()方法只拷贝对象中的基本数据类型,而不会拷贝数组、容器对象、引用对象等。若要实现深拷贝,必须将Prototype模式中的数组、容器对象、引用对象等另行拷贝。
例如,深拷贝一个对象时,该对象必须实现Cloneable接口并重写clone()方法,并在clone()方法内部将该对象引用的其他对象也克隆一份。同理,被引用的对象也要做同样的处理。
注意,Boolean、Byte、Character、Class、Double、Float、Integer、Long、Short、String以及大部分的Exception的子类均为不可变类,其对象没有必要实现深拷贝。此外,大部分的Java容器类都已实现克隆功能。
相较而言,clone()方法更适合数组深拷贝。但需要注意以下两点:
(1) 基本类型的一维数组可通过诸如(int[])data.clone()的形式克隆,而基本类型的"二维数组"(实际上是类型诸如int[]的一维数组)需要按维克隆。例如:
public static int[][] twoDimensionArrayClone (int[][] tdArray){
int[][] copy = tdArray.clone();
for (int i = 0; i < tdArray.length; i++) {
copy[i] = tdArray[i].clone();
}
return copy;
}
(2) 当数组元素为普通Java对象时,克隆数组后还要克隆数组中包含的对象。以下示例详细展示了数组对象的不同浅拷贝和深拷贝方式:
class Person implements Cloneable {
public String name;
public Person(String name) {this.name = name;}
@Override
public Object clone() {
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return object;
}
}
public class ArrayPrototype {
public static void main(String[] args) {
Person[] origin = new Person[] { new Person("Mike"), new Person("Jack"), new Person("Jason") };
Person[] copyOf = Arrays.copyOf(origin, origin.length); // 浅拷贝(内部调用System.arraycopy,返回新数组)
Person[] arrayCopy = new Person[origin.length]; // 浅拷贝(可拷贝部分数组)
System.arraycopy(origin, 0, arrayCopy, 0, origin.length);
Person[] clonedCopy = origin.clone(); // 浅拷贝
System.out.println("origin=copyOf=arrayCopy=clonedCopy=" +
(origin[0] == copyOf[0] && copyOf[1] == arrayCopy[1] && arrayCopy[2] == clonedCopy[2]));
clonedCopy[0].name = "Lily";
System.out.println("Shallow Person[0]: " + origin[0].name + " -> " + clonedCopy[0].name);
Person[] deepCopy = new Person[origin.length]; // 深拷贝
for(int i = 0; i < origin.length; i++) { deepCopy[i] = (Person) origin[i].clone(); }
deepCopy[1].name = "Lily";
System.out.println("Deep Person[1]: " + origin[1].name + " -> " + clonedCopy[1].name);
Person[] deepCopy2 = Arrays.stream(origin).map(Person::clone).toArray(Person[]::new); // 深拷贝
deepCopy2[2].name = "Lucy";
System.out.println("Deep Person[2]: " + origin[2].name + " -> " + deepCopy2[2].name);
//origin=copyOf=arrayCopy=clonedCopy=true
//Shallow Person[0]: Lily -> Lily
//Deep Person[1]: Jack -> Jack
//Deep Person[2]: Jason -> Lucy
}
}
业界实践
- Action对象(Struts2)
- prototype创建的bean实例(Spring)
Java设计模式:Prototype(原型)模式的更多相关文章
- java设计模式4——原型模式
java设计模式4--原型模式 1.写在前面 本节内容与C++语言的复制构造函数.浅拷贝.深拷贝极为相似,因此建议学习者可以先了解C++的该部分的相关知识,或者学习完本节内容后,也去了解C++的相应内 ...
- JAVA 设计模式之原型模式
目录 JAVA 设计模式之原型模式 简介 Java实现 1.浅拷贝 2.深拷贝 优缺点说明 1.优点 2.缺点 JAVA 设计模式之原型模式 简介 原型模式是六种创建型设计模式之一,主要应用于创建相同 ...
- java设计模式之原型模式
原型模式概念 该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.java中复制通过clone()实现的.clone中涉及深.浅复制.深.浅复制的概念如下: ⑴浅复制 ...
- 一天一个设计模式——Prototype 原型模式
一.模式说明 看了比较多的资料,对原型模式写的比较复杂,个人的理解就是模型复制,根据现有的类来直接创建新的类,而不是调用类的构造函数. 那为什么不直接调用new方法来创建类的实例呢,主要一个原因是如果 ...
- java设计模式之五原型模式(Prototype)
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通过对象的复制,进行讲解.在Java中 ...
- JAVA设计模式之 原型模式【Prototype Pattern】
一.概述: 使用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象. 简单的说就是对象的拷贝生成新的对象(对象的克隆),原型模式是一种对象创建型模式. 二.使用场景: 创建新的对象能够通过对 ...
- JAVA设计模式之原型模式(prototype)
原型模式: 原型模式又叫克隆模式 Java自带克隆模式 实现克隆模式必须实现Cloneable 接口,如果不实现会发生java.lang.CloneNotSupportedException异常 当某 ...
- 设计模式:Prototype 原型模式 - 同学你抄过别人的作业么?-clone()方法的使用
原型模式: 通过某个类的实例来创建对象 使用原型模式的好处: 好处是什么呢?当我们需要多次重复的创建一个类的示例的时候,我们可以使用new但是,new不仅仅耗费内存而且,如果new 某个类的构造方法中 ...
- 5.设计模式----prototype原型模式
原型模式:做到是原型,那肯定是自己本身才是原型,原型模式属于对象的创建模式. 关于原型模式的实现方式分2种: (1)简单形式.(2)登记形式,这两种表现形式仅仅是原型模式的不同实现. package ...
- JAVA 设计模式之 原型模式详解
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式利用的是克隆的原理,创建新的对象,JDK提供的Cloneable 和JSON. ...
随机推荐
- form表单中的button自动刷新页面问题
form表单中如果存在button的话,有可能会出现一个问题:点击button,触发了页面的自动刷新事件. 原因是因为<button>标签默认的类型是submit,即默认的button点击 ...
- Python 关于 pip 部分相关库的安装
下文中“:”后面安装的安装语句需要打开 cmd (命令提示符),在 cmd 中输入. 示例: 在搜索框输入 cmd,单机命令提示符: 然后输入安装语句,按回车键: 因为我之前已经装过了,所以这里显示的 ...
- SpringBoot整合Thymeleaf表单更新操作
对于表单值回显并更新的逻辑相比大家都已经很熟悉了, 但是我们操作Thymeleaf的话这里就会有一点小坑了, 在要回显值的表单的所有字段上,我们都要加上 th:field,才可以完成回显值更新 或者这 ...
- C# HttpClient设置cookies的两种办法
前言:最近公司使用HttpClient对象在发送请求,抛弃了之前的HttpWebRequest,使用httpClient有个好处:就是可以只使用一个HttpClient的实例,去完成发送所有的请求数据 ...
- Redis报错:ERR This instance has cluster support disabled
异常分析从报错误的信息ERR This instance has cluster support disabled很明显看得出来,是没有启动redis集群功能,可是我项目配置的集群的配置方式,要么修改 ...
- 资深程序员告诉你为什么要用Python3而不是Python2
经常遇到这样的问题:<现在开始学习python的话,是学习python2.x还是学习python3.x比较好?>,这也是许多初学者会遇到的问题,我们的答案是python 3.x. 为了帮助 ...
- CentOS 7安装配置MySQL 5.7
概述 前文记录了在Windows系统中安装配置MySQL 5.7(前文连接:https://www.cnblogs.com/Dcl-Snow/p/10513925.html),由于安装部署大数据环境需 ...
- Java开发人员必备十大工具
Java世界中存在着很多工具,从著名的IDE(例如Eclipse,NetBeans和IntelliJ IDEA)到JVM profiling和监视工具(例如JConsole,VisualVM,Ecli ...
- 【转载】Android App应用启动分析与优化
前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动 当启动应用时,后台没有该应用 ...
- Violet音乐社区界面原型手册
目录 Violet音乐社区界面原型手册 一.引言 1.0 项目前阶段相关文档 1.1 编写目的 1.2 开发背景 二.界面原型展示 2.0 界面设计说明 2.1 首页 2.2 歌单/专辑/单曲界面 2 ...