概念定义

使用原型实例指定待创建对象的种类,并通过拷贝该原型来创建新的对象。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. ospf邻居建立过程

    1. hello报文的作用 邻居发现:自动发现邻居路由器. 邻居建立:完成Hello报文中的参数协商,建立邻居关系. 邻居保持:通过Keepalive机制,检测邻居运行状态. hello报文的发送时间 ...

  2. 女朋友会 Python 是多么可怕的一件事!

    ​ 阅读文本大概需要 8 分钟. 1 目 标 场 景 女朋友会 Python 是多么可怕的一件事! 一位朋友告诉忽略了一件事,假设女朋友会 Python 的话,那岂不是要翻车?如果是这样的话,女朋友发 ...

  3. 如何完成述职报告或年终总结PPT

    对于我们 打工仔 职场人士来说,年有年度总结,月有月度报告,指不定有些小伙伴还会有周报和日报,不仅枯燥,而且浪费时间,头都要炸了 ,简直太有趣了呢. 所以,如何准确快速的写完述职报告呢? 这是个好问题 ...

  4. 使用pip安装python库的几种方式

    操作系统 : CentOS7.5.1804_x64 Python 版本 : 3.6.8 1.使用pip在线安装 1.1 安装单个package 格式如下: pip install SomePackag ...

  5. 使用Redis实现延时任务(二)

    前提 前一篇文章通过Redis的有序集合Sorted Set和调度框架Quartz实例一版简单的延时任务,但是有两个相对重要的问题没有解决: 分片. 监控. 这篇文章的内容就是要完善这两个方面的功能. ...

  6. Thinkphp 5.1.24 parseKey缺陷导致聚合注入 分析

    测试url: http://127.0.0.1/thinkphp/thinkphp_5.1.24/public/index.php/index/index/sqli2?id=2 控制器是获取id参数作 ...

  7. Java集合专题

    1.Java 中常用的容器有哪些? 常见容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表. Collectio ...

  8. Cesium专栏-空间分析之剖面分析(附源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  9. 记录SqlServer安装后局域网无法连接

    ,如果安装的时候 已经选择 “混合模式(SQL Server 身份验证和Windows 身份验证)(M)”, 并且已经为 SQL Server 系统管理员分配了账户和密码 则不用重新配置. 配置方法如 ...

  10. Redis—配置文件详解

    https://www.cnblogs.com/shizhengwen/p/9283973.html https://www.cnblogs.com/yangy608/p/4443665.html h ...