概念定义

使用原型实例指定待创建对象的种类,并通过拷贝该原型来创建新的对象。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(原型)模式的更多相关文章

  1. java设计模式4——原型模式

    java设计模式4--原型模式 1.写在前面 本节内容与C++语言的复制构造函数.浅拷贝.深拷贝极为相似,因此建议学习者可以先了解C++的该部分的相关知识,或者学习完本节内容后,也去了解C++的相应内 ...

  2. JAVA 设计模式之原型模式

    目录 JAVA 设计模式之原型模式 简介 Java实现 1.浅拷贝 2.深拷贝 优缺点说明 1.优点 2.缺点 JAVA 设计模式之原型模式 简介 原型模式是六种创建型设计模式之一,主要应用于创建相同 ...

  3. java设计模式之原型模式

    原型模式概念 该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.java中复制通过clone()实现的.clone中涉及深.浅复制.深.浅复制的概念如下: ⑴浅复制 ...

  4. 一天一个设计模式——Prototype 原型模式

    一.模式说明 看了比较多的资料,对原型模式写的比较复杂,个人的理解就是模型复制,根据现有的类来直接创建新的类,而不是调用类的构造函数. 那为什么不直接调用new方法来创建类的实例呢,主要一个原因是如果 ...

  5. java设计模式之五原型模式(Prototype)

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通过对象的复制,进行讲解.在Java中 ...

  6. JAVA设计模式之 原型模式【Prototype Pattern】

    一.概述: 使用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象. 简单的说就是对象的拷贝生成新的对象(对象的克隆),原型模式是一种对象创建型模式. 二.使用场景: 创建新的对象能够通过对 ...

  7. JAVA设计模式之原型模式(prototype)

    原型模式: 原型模式又叫克隆模式 Java自带克隆模式 实现克隆模式必须实现Cloneable 接口,如果不实现会发生java.lang.CloneNotSupportedException异常 当某 ...

  8. 设计模式:Prototype 原型模式 - 同学你抄过别人的作业么?-clone()方法的使用

    原型模式: 通过某个类的实例来创建对象 使用原型模式的好处: 好处是什么呢?当我们需要多次重复的创建一个类的示例的时候,我们可以使用new但是,new不仅仅耗费内存而且,如果new 某个类的构造方法中 ...

  9. 5.设计模式----prototype原型模式

    原型模式:做到是原型,那肯定是自己本身才是原型,原型模式属于对象的创建模式. 关于原型模式的实现方式分2种: (1)简单形式.(2)登记形式,这两种表现形式仅仅是原型模式的不同实现. package ...

  10. JAVA 设计模式之 原型模式详解

    原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式利用的是克隆的原理,创建新的对象,JDK提供的Cloneable 和JSON. ...

随机推荐

  1. form表单中的button自动刷新页面问题

    form表单中如果存在button的话,有可能会出现一个问题:点击button,触发了页面的自动刷新事件. 原因是因为<button>标签默认的类型是submit,即默认的button点击 ...

  2. Python 关于 pip 部分相关库的安装

    下文中“:”后面安装的安装语句需要打开 cmd (命令提示符),在 cmd 中输入. 示例: 在搜索框输入 cmd,单机命令提示符: 然后输入安装语句,按回车键: 因为我之前已经装过了,所以这里显示的 ...

  3. SpringBoot整合Thymeleaf表单更新操作

    对于表单值回显并更新的逻辑相比大家都已经很熟悉了, 但是我们操作Thymeleaf的话这里就会有一点小坑了, 在要回显值的表单的所有字段上,我们都要加上 th:field,才可以完成回显值更新 或者这 ...

  4. C# HttpClient设置cookies的两种办法

    前言:最近公司使用HttpClient对象在发送请求,抛弃了之前的HttpWebRequest,使用httpClient有个好处:就是可以只使用一个HttpClient的实例,去完成发送所有的请求数据 ...

  5. Redis报错:ERR This instance has cluster support disabled

    异常分析从报错误的信息ERR This instance has cluster support disabled很明显看得出来,是没有启动redis集群功能,可是我项目配置的集群的配置方式,要么修改 ...

  6. 资深程序员告诉你为什么要用Python3而不是Python2

    经常遇到这样的问题:<现在开始学习python的话,是学习python2.x还是学习python3.x比较好?>,这也是许多初学者会遇到的问题,我们的答案是python 3.x. 为了帮助 ...

  7. CentOS 7安装配置MySQL 5.7

    概述 前文记录了在Windows系统中安装配置MySQL 5.7(前文连接:https://www.cnblogs.com/Dcl-Snow/p/10513925.html),由于安装部署大数据环境需 ...

  8. Java开发人员必备十大工具

    Java世界中存在着很多工具,从著名的IDE(例如Eclipse,NetBeans和IntelliJ IDEA)到JVM profiling和监视工具(例如JConsole,VisualVM,Ecli ...

  9. 【转载】Android App应用启动分析与优化

    前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动  当启动应用时,后台没有该应用 ...

  10. Violet音乐社区界面原型手册

    目录 Violet音乐社区界面原型手册 一.引言 1.0 项目前阶段相关文档 1.1 编写目的 1.2 开发背景 二.界面原型展示 2.0 界面设计说明 2.1 首页 2.2 歌单/专辑/单曲界面 2 ...