设计模式——Prototype(原型)
在工厂模式、建造者模式等中,我们使用了不同的构造方法(各种Factory或者Builder)去代替或者说掩盖Java语言之中“new”这个操作来创建对象实例。Java中要创建一个新的对象并不一定只能靠“new”这个关键字的,我们还有“clone()”。
在接触原型模式之前,我们先来了解一下克隆一些知识:
?1.clone()方法在Java中从Object类开始就具备,并且作为原生(Native)方法出现。它默认是protected的,因此在被子类提升可见性之前,无法被外界使用。
2.所有需要进行克隆操作的类都必须实现Cloneable接口,这个接口与Serializable接口一样,没有任何需要实现的方法,仅仅是作为一个标示。
3.所有数组都实现了Cloneable接口,并且已经提升了clone()方法的可见性至public,换句话说数组对象是可以直接调用clone()方法的。
4.克隆分为浅拷贝和深拷贝两种。
浅拷贝:操作基本上可以理解为只拷贝存储于栈中的内容,包括对象中简单类型的数据、指向其他复杂对象的指针等,但是不会将指向的复杂对象也拷贝一次。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深拷贝:被复制对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把重复的对象所引用的对象都复制一遍,而这种对被引用到的对象的复制叫做间接复制。
原型模式定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
浅拷贝:
抽象原型:
public interface Prototype {public Object cloneMe() throws CloneNotSupportedException;}
?
具体原型:
public class Cubic implements Prototype, Cloneable {double length, width, height;public Cubic(double length, double width, double height) {this.length = length;this.width = width;this.height = height;}@Overridepublic Object cloneMe() throws CloneNotSupportedException {//调用从Object类继承的clone()方法; Cubic object=(Cubic) clone(); return object;}}
? ? ? ?模式使用:
public class CubicTest {public static void main(String[] args) { Cubic cubic = new Cubic(12, 20, 66); System.out.println("cubic的长、宽和高:"); System.out.println(cubic.length + "," + cubic.width + "," + cubic.height); try { Cubic cubicopy = (Cubic) cubic.cloneMe(); System.out.println("cubicopy的长、宽和高:"); System.out.println(cubicopy.length + "," + cubicopy.width + "," + cubicopy.height); } catch (Exception e) { e.printStackTrace(); }}}
?
? ? ? 深拷贝:
? ? ? 具体原型:
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class Goat implements Prototype, Serializable {StringBuffer color;public StringBuffer getColor() {return color;}public void setColor(StringBuffer color) {this.color = color;}@Overridepublic Object cloneMe() throws CloneNotSupportedException {Object object = null;try {ByteArrayOutputStream outOne = new ByteArrayOutputStream();ObjectOutputStream outTwo = new ObjectOutputStream(outOne);// 将原型吸入对象输出流outTwo.writeObject(this);ByteArrayInputStream inOne = new ByteArrayInputStream(outOne.toByteArray());ObjectInputStream inTwo = new ObjectInputStream(inOne);// 创建新对象:原型复制品object = inTwo.readObject();} catch (Exception e) {e.printStackTrace();}return object;}}
? ? ? ? 模式使用:
public class GoatTest {public static void main(String[] args) {Goat goat = new Goat();goat.setColor(new StringBuffer("白色的山羊"));System.out.println("goat 是" + goat.getColor());try {Goat goatCopyGoat = (Goat) goat.cloneMe();System.out.println("goatCopy是" + goatCopyGoat.getColor());System.out.println("goatCopy将自己的颜色改变成黑色");goatCopyGoat.setColor(new StringBuffer("黑色的山羊"));System.out.println("goat仍然是" + goat.getColor());System.out.println("goatCopy是" + goatCopyGoat.getColor());} catch (Exception e) {e.printStackTrace();}}}
总结:
Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone()方法替我们做了绝大部分事情。在其他语言之中,一种比较简单的方法是可以考虑使用序列化技术(Serilization)来完成对象的复制。
?在Java语言中clone()方法完成的是浅拷贝的克隆,如果需要深拷贝,也可以考虑使用序列化来完成,当然这样比起Native的clone()方法来说,效率差了很多,所以更加推荐的方法是针对类中包含的复杂对象情况,重写clone()方法,多次调用父类的clone()来完成,虽然要多写不少代码,但是保证了效率。