欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式之原型模式prototype

程序员文章站 2022-06-13 16:04:52
...
所谓的原型模式,就是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
1.首先需要了解java的clone技术
我理解的很简单,无非是把一个对象进行复制粘贴。那么,来看一下JAVA语言中是如何来实现的这个步骤的。我们依次需要知道以下这些事。

1.Object对象中有一个clone()方法。而且是protected。

2.继承于Object的类一般都可以实现这个方法(有特殊的,比如StringBuffer等,官方定的,不去研究它)。

3.想被clone的对象需要实现接口Cloneable。如果此对象的类不能实现接口Cloneable,则会抛出CloneNotSupportedExcetion。

4.所有的数组都被视为实现接口Cloneable。

5.clone分为浅clone(又称影子clone)和深clone。

6.Object类本身不实现接口Cloneable,所以在类为Object的对象上调用clone方法,将会导致在运行时抛出异常。

关键所在深克隆和浅克隆,那到底他们之间有什么区别呢?
首先看一下例子:


public abstract class CloneClass implements Cloneable {
public int aInt;

public Object clone() {
CloneClass o = null;
try {
o = (CloneClass) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}
}





public class UnCloneA {
private int i;

public UnCloneA(int ii) {
i = ii;
}

public void doublevalue() {
i *= 2;
}

public String toString() {
return Integer.toString(i);
}
}




public class CloneB implements Cloneable {
public int aInt;
public UnCloneA unCloneA = new UnCloneA(111);

public Object clone() {
CloneB o = null;
try {
o = (CloneB) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}
}




public class Test {
public static void main(String args[]) {
CloneB b1 = new CloneB();
b1.aInt = 11;
System.out.println("before clone,b1.aInt = " + b1.aInt);
System.out.println("before clone,b1.unCA = " + b1.unCloneA);
CloneB b2 = (CloneB) b1.clone();
b2.aInt = 22;
b2.unCloneA.doublevalue();
System.out.println("=================================");
System.out.println("after clone,b1.aInt = " + b1.aInt);
System.out.println("after clone,b1.unCA = " + b1.unCloneA);
System.out.println("=================================");
System.out.println("after clone,b2.aInt = " + b2.aInt);
System.out.println("after clone,b2.unCA = " + b2.unCloneA);

}
}


输出结果:

before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 222
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222

可见,当执行clone这个动作的时候,系统首先开辟一个和它一样的空间。然后分别对其内容进行复制。复制过程中,如果是基本类型,没什么说的,直接把值复制过来。如果不是基本类型,复制的则是该类型对象的引用。

这样的clone就是所谓的浅clone。那很显然,如果上面复制的过程中,对于非基本类型实现的不是一个引用复制,而是创建一个新的一样的对象(其实也是一个clone步骤),那么就是所谓的深clone。对于深度克隆只不过是clone的一种扩展,还有N深clone等等。对于这些,和我们要研究的原型模式没有多少关系。

1、浅克隆:

a. 提供一个类作为克隆的原型,该类实现了Clonable接口

b. 在该类中覆盖Object类的clone()方法,采用super.clone()方法完成克隆

c. 在外部使用场合先产生一个原型对象,然后调用其clone()方法产生克隆对象

2、深克隆:

a. 提供一个类作为克隆的原型,该类实现了Serializable接口

b. 在该类中覆盖Object类的clone()方法,采用序列化的方法完成克隆(对象流的读写)

c.在外部使用场合先产生一个原型对象,然后调用其clone()方法产生克隆对象


二、Prototype模式与深、浅克隆:

使用Object.clone()方法进行的克隆是“浅克隆”-被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。浅克隆的步骤如下:

①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。

②在派生类中覆盖基类的clone()方法,并声明为public。

③在派生类的clone()方法中,调用super.clone()。

④在派生类中实现Cloneable接口。


为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()要识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。

要实现“深克隆”-被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

深克隆需要使用Java中提供的对象串行化功能-即把要复制的对象写入到一个缓冲流,然后通过输入流读入,完成对象的复制

下面是深克隆与浅克隆的代码

public class SuperStar implements Cloneable, Serializable {
public String name;
public int height;
public Skill mySkill;

public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}

public Object deepClone() throws Exception {
Object o = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); // (ByteArrayOutputStream)此类实现了一个输出流,其中
// 的数据被写入一个字节数组。缓冲区会随着数据的不断写入而自动增长。
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this); // 通过Serialization机制将自身写入该缓冲区

// 找到刚才开辟的缓冲区准备读取
ByteArrayInputStream bis = new ByteArrayInputStream(bos
.toByteArray()); //
ObjectInputStream ois = new ObjectInputStream(bis);
// 将刚才写入的内容读入一个新的对象
o = ois.readObject();

bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}

}



public class Skill implements Serializable{
public int strength;
public int jumping;
}



public class Test {
public static void main(String args[]) throws Exception{
SuperStar s1=new SuperStar();
s1.name="姚明";
s1.height=226;
s1.mySkill=new Skill();
s1.mySkill.jumping=50;
s1.mySkill.strength=20;

System.out.println(s1.name+" " + s1.height +" "+s1.mySkill.jumping);

SuperStar s2=(SuperStar) s1.clone();

// SuperStar s2=(SuperStar) s1.deepClone();
s2.name="潘长江";
s2.height=100;
s2.mySkill.jumping=10;
s2.mySkill.strength=5;

System.out.println(s2.name+" " + s2.height+" " +s2.mySkill.jumping);
System.out.println(s1.name+" " + s1.height +" "+s1.mySkill.jumping);
}
}


浅克隆 结果
姚明 226 50
潘长江 100 10
姚明 226 10


深克隆 结果

姚明 226 50
潘长江 100 10
姚明 226 50

JAVA中的clone方法,其实现是native的。这也就意味着它的执行效率是远高于new一个新对象的。所以,当需要生成大量相似对象的时候,可以考虑下应用clone,也就是原形模式。