设计模式-原型模式

一、原型模式的核心思想

原型模式(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 模式,我们可以不了解原型对象的任何细节及它内部的层次结构,快速克隆出一个个同样的对象来,而且这些对象间无影响,但是我们必须要实现它特定的接口。

三、何时使用原型模式

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

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

相关推荐
wrx繁星点点1 小时前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
金池尽干3 小时前
设计模式之——观察者模式
观察者模式·设计模式
也无晴也无风雨3 小时前
代码中的设计模式-策略模式
设计模式·bash·策略模式
捕鲸叉12 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
wrx繁星点点12 小时前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
凉辰13 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
菜菜-plus13 小时前
java设计模式之策略模式
java·设计模式·策略模式
暗黑起源喵13 小时前
设计模式-迭代器
设计模式
lexusv8ls600h14 小时前
微服务设计模式 - 网关路由模式(Gateway Routing Pattern)
spring boot·微服务·设计模式
sniper_fandc17 小时前
抽象工厂模式
java·设计模式·抽象工厂模式