创建型模式-原型模式(对象的克隆)

1. 定义

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

2. 结构

原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。结构如图:

原型模式

在原型模式结构图中包含如下几个角色:

  • Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
  • ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
  • Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

3. 代码实现

1 对象的克隆方法

  • 通用实现方法

通用的克隆实现方法是在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新创建的对象中,保证它们的成员属性相同。代码如下:

public class ConcretePrototype implements Prototype {

	private String attr;

	public String getAttr() {
		return attr;
	}

	public void setAttr(String attr) {
		this.attr = attr;
	}

	@Override
	public Prototype clone() {
		ConcretePrototype concretePrototype = new ConcretePrototype();
		concretePrototype.setAttr(this.attr);
		return concretePrototype;
	}
}
  • Java语言的clone()方法

代码如下:

class ConcretePrototype2 implements Cloneable {

	@Override
	public Prototype clone() {
		Object object = null;
		try {
			object = super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return (Prototype) object;
	}

}

2 浅克隆与深克隆的实现,代码如下:

public class WeeklyLog implements Cloneable,Serializable {

	private static final long serialVersionUID = 8532521622598526741L;

	private String name;

	private String content;

	private Attachment attachment;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public Attachment getAttachment() {
		return attachment;
	}

	public void setAttachment(Attachment attachment) {
		this.attachment = attachment;
	}

	@Override
	//浅克隆,克隆出来的对象中的Attachment指向同一个对象地址
	public WeeklyLog clone(){
		Object obj;
		try
		{
			obj = super.clone();
			return (WeeklyLog)obj;
		}
		catch(CloneNotSupportedException  e)
		{
			System.out.println("不支持复制!");
			return null;
		}
	}

	//深克隆,克隆出来的对象中的Attachment为不同的对象
	public WeeklyLog deepClone() throws IOException, ClassNotFoundException {
		//将对象写入流中
		ByteArrayOutputStream bao=new  ByteArrayOutputStream();
		ObjectOutputStream oos=new  ObjectOutputStream(bao);
		oos.writeObject(this);

		//将对象从流中取出
		ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());
		ObjectInputStream ois=new  ObjectInputStream(bis);
		return  (WeeklyLog)ois.readObject();
	}
}



class Attachment {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,作为一种标识,本质上是调用的Object类的方法

3 利用原型模式可实现一个原型管理器,专门负责克隆相应的对应,代码如下:

public class PrototypeManager {

	private static PrototypeManager instance = new PrototypeManager();

	private Map<String, OfficialDocument> hm = new ConcurrentHashMap<>();

	private PrototypeManager() {
		hm.put("far", new FAR());
		hm.put("srs", new SRS());
	}

	public PrototypeManager getInstance() {
		return instance;
	}

	//增加新的公文对象
	public void addOfficialDocument(String key, OfficialDocument doc) {
		hm.put(key, doc);
	}

	//通过浅克隆获取新的公文对象
	public OfficialDocument getOfficialDocument(String key) {
		return ((OfficialDocument) hm.get(key)).clone();
	}

}

//抽象公文接口,也可定义为抽象类,提供clone()方法的实现,将业务方法声明为抽象方法
interface OfficialDocument extends Cloneable {

	OfficialDocument clone();

	void display();
}

//可行性分析报告(Feasibility Analysis Report)类
class FAR implements OfficialDocument {

	@Override
	public OfficialDocument clone() {
		OfficialDocument far = null;
		try {
			far = (OfficialDocument) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println("不支持复制!");
		}
		return far;
	}

	@Override
	public void display() {
		System.out.println("《可行性分析报告》");
	}
}

//软件需求规格说明书(Software Requirements Specification)类
class SRS implements OfficialDocument {

	@Override
	public OfficialDocument clone() {
		OfficialDocument srs = null;
		try {
			srs = (OfficialDocument) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println("不支持复制!");
		}
		return srs;
	}

	@Override
	public void display() {
		System.out.println("《软件需求规格说明书》");
	}
}

4. 优缺点

  • 优点
  1. 如果创建新的对象比较复杂时,可利用原型模式简化对象的创建过程,通过复制也可提高效率。
  2. 原型模式提供了简化的创建结构,无须专门的工厂类来创建产品
  3. 可以使用深克隆保持对象的状态。
  • 缺点
  1. 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
  2. 实现深克隆时需要编写较为复杂的代码,多重嵌套引用时实现起来比较麻烦。

5. 适用场景

  1. 创建新对象成本较大(初始化时间较长或占用资源较长),可通过原型模式对已有对象进行复制得到。
  2. 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
  3. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

6. 个人理解

用来复制一个对象,无需知道具体创建过程,可进行对象的浅克隆或深克隆。

参考

  1. Java设计模式-刘伟
posted @ 2018-09-18 11:20  David丁  阅读(472)  评论(0编辑  收藏  举报