原型模式(克隆)

原型模式是一种较为简单的设计模式

描述

原型模式是一个创建型的模式,原型就是可供复制的模板,通过复制原型得到一个新的实例,这个过程我们俗称为“克隆”。被复制的实例就是我们所说的“原型”。

什么地方比较适合使用?

  • 类初始化时需要消耗较多的资源,包括数据,硬件资源等,通过拷贝避免这些消耗。
  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限。
  • 需要大量相同的对象

简单实现

我们有一个文档类,类中包含标题和文字如下:

class Document implements Cloneable{
	private String mName;
	private List<String> mTexts = new ArrayList<>();
	
	public String getmName() {
		return mName;
	}

	public void setmName(String mName) {
		this.mName = mName;
	}

	public List<String> getmTexts() {
		return mTexts;
	}

	public void addMtexts(String mTexts) {
		this.mTexts.add(mTexts);
	}
	
	public void setMtexts(List<String> mTexts) {
		this.mTexts = mTexts;
	}

	@Override
	protected Document clone()  {
		// TODO Auto-generated method stub
		try {
			Document document = (Document) super.clone();
			document.mName = this.mName;
			document.mTexts = this.mTexts;
			return document;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public String toString() {
		return "Document [mName=" + mName + ", mTexts=" + mTexts + "]";
	}
}

上面时克隆模式的一种简单实现。我们通过实现Cloneable接口和覆写clone方法来实现对象的拷贝,需要注意的时clone方法并不是Cloneable接口中的,而是Object中的方法,Cloneable只是一个标识接口,用来表明该类是可以拷贝的,而拷贝的具体实现是在clone方法中的(如果没有实现Cloneable接口直接调用clone方法将会抛出异常)。

下面我们来试用一下卡隆模式:

public static void main(String[] args) {
		// TODO Auto-generated method stub

		Document document = new Document();
		document.setmName("文档");
		document.addMtexts("文档1");
		document.addMtexts("文档2");
		document.addMtexts("文档3");
		System.out.println(document.toString());
		
		//生成拷贝
		Document documentClone = document.clone();
		documentClone.setmName("克隆");
		System.out.println(document.toString());
		System.out.println(documentClone.toString());
		
	}

输出:

Document [mName=文档, mTexts=[文档1, 文档2, 文档3]]
Document [mName=文档, mTexts=[文档1, 文档2, 文档3]]
Document [mName=克隆, mTexts=[文档1, 文档2, 文档3]]

从上面的输出可以看出,通过clone方法确实时复制了一份document对象,里面的文字内容是一样的,我们修改了documentClone对象的name,所以我们看到它改变了,它并不会影响document将对象中的name值。

克隆模式真的这么简单吗?如果我们修改documentClone中文字会怎样呢?
在这里插入图片描述
运行结果:

Document [mName=文档, mTexts=[文档1, 文档2, 文档3]]
Document [mName=文档, mTexts=[文档1, 文档2, 文档3, 追加文档]]
Document [mName=克隆, mTexts=[文档1, 文档2, 文档3, 追加文档]]

从结果中我们可以看出修改documentClone对象中的内容document对象中的内容也跟着需改了,这是什么情况?下面我们就来聊一聊这个问题。

浅拷贝与深拷贝

上面的例子中我们实际上实现的只是一个浅拷贝,这份拷贝实际上并不是将原始数据所有字段都重新构造了一份,而是副本中的字段引用指向原始文档中的字段,如下:
在这里插入图片描述

A引用B就是说两个对象指向同一个地址,当修改A时B也会发生改变,同时修改B时A也会跟着变化。

那如何修改这种浅拷贝呢(变为深拷贝)?

我们需要重写clone方法来达到我们的目的,如下:

@Override
	protected Document clone()  {
		// TODO Auto-generated method stub
		try {
			Document document = (Document) super.clone();
			document.mName = this.mName;
			document.mTexts = (ArrayList)this.mTexts.clone();
			return document;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

mTexts 对象是一份拷贝,而不是一个地址指向,这就实现了深度拷贝,我们再来运行一下,运行结果如下:

Document [mName=文档, mTexts=[文档1, 文档2, 文档3]]
Document [mName=文档, mTexts=[文档1, 文档2, 文档3]]
Document [mName=克隆, mTexts=[文档1, 文档2, 文档3, 追加文档]]

我们会发现再修改documentClone对象,不再对document对象产生影响了。这归功于ArrayList类中实现了Cloneable接口并重写了clone方法,内部实现如下:

在这里插入图片描述

需要强调一点的是:实现cloneable和复写clone方法只是原型模式的一种实现方式。

原创文章 63 获赞 59 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013049016/article/details/98511108
今日推荐