首页 技术 正文
技术 2022年11月7日
0 收藏 555 点赞 1,125 浏览 3747 个字

概念定义

使用原型实例指定待创建对象的种类,并通过拷贝该原型来创建新的对象。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)
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,489
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,904
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,737
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,490
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,128
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,290