设计模式-原型模式

一、原型模式的核心思想

原型模式(PrototypePatterm)是创建型模式的一种,其特点在于通过"复制"一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的"原型",这个原型是可定制的。

用面向对象的方法来说就是,我们先建立一个原型,然后通过对原型进行复制和修饰的方法,就可以产生一个与原型相似的新对象。即:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

在Java中复制模型对象是通过clone0方法实现的。其实,这个方法可以是任意名字,比如cloneA0、cloneBO等。不过,一般应该使用clone0方法,这样做有两个原因:一是出于习惯,复制对象当然应该是 clone0;二是,在许多语言中的基础类中,比如作为所有类基础的Obiect,都定义了clone()方法。因此,实现原型模式的方法一般应该是原型类继承了Cloneable 接口,在具体原型类中需要实现 clone0方法,完成对象的自我复制。如下程序是一个典型的原型类:

原型类Prototype.iava

java 复制代码
package creation.prototype;

/ **
*  @author Minggg
*  原型模式
* /
public class Prototype implements Cloneable {

	/ **
	* 浅复制
	* /
	public Object clone() throws CloneNotSupportedException {
		Prototype prototype = (Prototype) super.clone();
		return prototype;
	}
}

调用 Prototype 模式很简单:

java 复制代码
Prototype obj = new Prototype();
Prototype obj2 =obj.clone();//复制

当然也可以结合工厂模式来创建 Prototype 实例。

二、浅复制与深复制

从以上的使用可以看出,在Java中Prototype 模式变成 clone()方法的使用,此方法执行的是该对象的"浅复制",而不"深复制"操作。

因此,复制分为两种:复制浅和深复制。

  • 浅复制:将一个对象复制之后,生成一个新的对象,新对象的所有成员变量(基本类型或引用类型)都含有与原有对象相同的值,如果原有对象的成员变量是基本数据类型,就会将这个变量的值复制一份到新对象里面,如果原有对象的成员变量是引用数据类型,那么这个引用指向的对象不会新生成一份,而是,在新对象里面的这个引用与原有对象的引用指向的是同一个对象。
  • 深复制:将一个对象复制之后,生成一个新的对象,新对象的基本数据类型变量含有与原有对象相同的值,如果原有对象的成员变量是引用数据类型,在新对象里面,这些引用变量将指向被复制过的新对象,而不再是指向原有的那些被引用的对象,深复制把要复制的对象所引用的对象都复制一遍。

为了实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出该二进制数据对应的对象。

为了展示浅复制和深复制在Java中的区别,我们新建一个类,其中包含了一个基本数据类型String 变量和一个引用数据类型SerializableObject的变量。

  • 在执行浅复制时,String变量的值会被复制一份,而SerializableObiect变量只会引用同一个对象。
  • 在执行深复制时,String变量和SerializableObiect变量的值都会被复制一份。

完整的代码如下程序所示:

深复制 DeepPrototype.java

java 复制代码
package creation.prototype;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepPrototype implements Cloneable, Serializable {

	private static final long serialVersionUID=1L;
	private String str;
	private SerializableObject obj;
	
	/**
	* 浅复制
	*/
	public Object clone() throws CloneNotSupportedException {
		DeepPrototype prototype = (DeepPrototype) super.clone();
		return prototype;
	}


	/**
	*深复制
	*/
	public Obiect deepClone() throws IOException,ClassNotFoundException {
		// 写入当前对象的二进制流
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(this);
		
		//读出二进制流产生新的对象
		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray();
		ObjectInputStream ois = new ObjectInputStream(bis);
		return ois.readObject();
	}

	public String getStr() {
		return str;
	}
	
	public void setStr(String str){
		this.str = str;
	}
	
	public SerializableObject getObj(){
		return obj;
	}
	
	public void setObj(SerializableObject obj){
		this.obj = obj;
	}


	class SerializableObject implements Serializable {
		private static final long serialVersionUID=1L
	}
}

下面我们来创建一个 DeepPrototype对象pt,并分别使用浅复制和深复制来复制一个新的对象pt1 和 pt2,并修改 String 的值,查看输出的结果有什么不同。完整的代码如下程序所示:

测试程序 PrototypeTest.java

java 复制代码
package creation.prototype;

import java.io.IOException;

public class DeepPrototypeTest {

	public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
		// 创建对象
		DeepPrototype pt = new DeepPrototype();
		pt.setStr("Hello World!");
		SerializableObject obj= new SerializableObject();
		pt.setObj(obj);
		
		// 浅复制
		DeepPrototype pt1 =(DeepPrototype) pt.clone();
		System.out.printIn("浅复制原对象 str:"+pt.getStr());
		System.out.println("浅复制新对象 str:"+pt1.getStr());
		System,out.println("浅复制原对象 obj:"+pt.getObj());
		System.out.println("浅复制新对象 obj:"+pt1.getObj());
		
		// 修改浅复制对象的值
		pt1 .setStr("Hello China!");
		System,out.println("修改后浅复制原对象 str:"+ pt.getStr());
		System.out.println("修改后浅复制新对象 str:"+ pt1.getStr());
		System,out.println("修改后浅复制原对象 obj:"+ pt.getObj()):
		System.out.println("修改后浅复制新对象 obj:"+ pt1.getObj());
		
		// 深复制
		DeepPrototype pt2 = (DeepPrototype) pt.deepClone();
		System.out.println("深复制原对象 str:"+pt.getStr());
		System.out.println("深复制新对象 str:"+pt2.getStr());
		System.out.println("深复制原对象 obj:"+pt.getObj());
		System.out.println("深复制新对象 obj:"+pt2.getObj());
		
		// 修改深复制对象的值
		pt2.setStr("Hello China!");
		System.out.println("修改后深复制原对象 str:"+pt.getStr());
		System.out.println("修改后深复制新对象 str:"+pt2.getStr());
		System.out.println("修改后深复制原对象 obj:"+pt.getObj());
		System.out.println("修改后深复制新对象 obj:"+pt2.getObj());
	}

}

运行该程序的结果为:

java 复制代码
浅复制原对象 str:Hello World!
浅复制新对象 str:Hello World!
浅复制原对象 obj:creation.prototype.SerializableObject@lfb8ee
浅复制新对象 obj:creation.prototype.SerializableObject@lfb8ee
修改后浅复制原对象 str:Hello World!
修改后浅复制新对象 str:Hello China!
修改后浅复制原对象obj:creation,prototype,SerializableObject@1fb8ee
修改后浅复制新对象obj:creation.prototype.SerializableObject@1fb8ee3
深复制原对象 str:Hello World!
深复制新对象 str:Hello World!
深复制原对象 obj:creation.prototype.SerializableObject@1fb8ee3
深复制新对象 obj:creation.prototype.SerializableObject@ld58aac
修改后深复制原对象 str:Hello World!
修改后深复制新对象 str:Hello China!
修改后深复制原对象 obj:creation.prototype.SerializableObject@1fb8ee
修改后深复制新对象 obj:creation.prototype.SerializableObject@1d58aa

从结果可以看出,浅复制的对象对于简单对象Sting的值可以不同,但引用类型obj的值却没有改变;而深复制的str和obi的值都被复制了一份。

因此,使用Obiect.clone()方法只能浅层次的克隆,即只能对那些成员变量是基本类型或String类型的对象进行克隆,对那些成员变量是类类型的对象进行克隆要使用到对象的序列化,不然克降出来的 Prototype 对象都共享同一个 obj 实例。

总而言之,Prototype 模式为我们提供另外一种高效创建对象的方法。使用Prototype 模式,我们可以不了解原型对象的任何细节及它内部的层次结构,快速克隆出一个个同样的对象来,而且这些对象间无影响,但是我们必须要实现它特定的接口。

三、何时使用原型模式

原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。

原型模式也是一种创建型模式,它关注的是大量相似对象的创建问题。我们经常会遇到这样的情况:在系统中要创建大量的对象,这些对象之间具有几乎完全相同的功能,只是在细节上有一点儿差别。

相关推荐
海特伟业11 小时前
隧道调频广播覆盖-隧道调频广播无线覆盖系统建设要点、难点分析与解决应对
运维·设计模式
sg_knight11 小时前
设计模式实战:享元模式(Flyweight)
python·设计模式·享元模式·flyweight
Swift社区14 小时前
AI 时代,ArkUI 的设计模式会改变吗?
人工智能·设计模式
数据中穿行14 小时前
访问者设计模式全方位深度解析
设计模式
宁雨桥15 小时前
前端设计模式面试题大全
前端·设计模式
数据中穿行16 小时前
迭代器设计模式全方位深度解析
设计模式
数据中穿行16 小时前
观察者设计模式全方位深度解析
设计模式
程序员Terry16 小时前
别老写重复代码了!模版方法模式一次讲透
java·设计模式
数据中穿行16 小时前
建造者模式全方位深度解析
设计模式