原型模式
一、简介
主要是用于创建重复的对象,同时又能保证性能。依旧是创建型模式。
比如你想要复制一个飞机,于是你模仿飞机做了一个壳子,但是这个飞机是飞不起来的,因为飞机内部的细节的元器件你是看不到的。
我们复制类的时候也是这样,当一个类里的成员是私有时,或者当一个类以接口的形式传过来时,我们只知道它是某个接口的实现类,并不知道具体是哪个类。当出现上述情况时,就无法复制。
这个是原型模式的类图
可以看到首先定义了一个prototype的接口,对于其他类,如果有克隆自己的需求,那么就会去实现这个接口。客服端通过调用克隆方法获得大量的克隆体。
二、代码实现
首先构造一个prototype接口
1 2 3 4 5
| package Prototype;
public interface Prototype { Object clone(); }
|
然后写一个Plane类并实现clone方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package Prototype;
public class Plane implements Prototype { private String name; private String type;
public Plane(){ name = "name" +Math.random(); type = "type" +Math.random(); }
public Plane(Plane plane){ this.name = plane.name; this.type = plane.type; }
public String getName() { return name; }
public String getType() { return type; }
@Override public Object clone() { return new Plane(this); } }
|
在main里尝试调用
1 2 3 4 5 6 7 8 9 10
| package Prototype;
public class Main { public static void main(String[] args) { Plane plane = new Plane(); System.out.println(plane.getName()+" "+plane.getType()); Plane clone = (Plane) plane.clone(); System.out.println(clone.getName()+" "+clone.getType()); } }
|
最后运行的结果属性值的确相同
三、浅拷贝和深拷贝
但是当我稍微修改一下代码,在Plane类里添加一个数组a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class Plane implements Prototype { private String name; private String type; private int a[];
public Plane(){ name = "name" +Math.random(); type = "type" +Math.random(); a = new int[]{1, 2, 3, 4, 5}; }
public Plane(Plane plane){ this.name = plane.name; this.type = plane.type; this.a = plane.a; }
public String getName() { return name; }
public String getType() { return type; }
public int getA0() { return a[0]; }
public void setType(String tmp) { type = tmp; }
public void setA0(int tmp) { a[0] = tmp; }
@Override public Object clone() { return new Plane(this); } }
|
再次运行修改后的main代码
1 2 3 4 5 6 7 8 9 10
| public class Main { public static void main(String[] args) { Plane plane = new Plane(); System.out.println(plane.getName()+" "+plane.getType()+" "+plane.getA0()); Plane clone = (Plane) plane.clone(); clone.setA0(100); System.out.println(plane.getName()+" "+plane.getType()+" "+plane.getA0()); System.out.println(clone.getName()+" "+clone.getType()+" "+plane.getA0()); } }
|
结果是这样的
很明显上述代码的clone()是一个浅拷贝
事实上常见的原型模式方法,即重写Cloneable接口里的clone()方法,也是一种浅拷贝
1 2 3 4 5 6 7 8 9
| public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; }
|
那么如何实现深克隆呢?
方法一:重写clone()方法,依次调用成员变量的clone方法,或者是手动开辟一个新的对象(比如下述代码就是建了一个新数组并复制元素)
1 2 3 4 5 6 7
| public Plane(Plane plane){ this.name = plane.name; this.type = plane.type; this.a = new int[plane.a.length]; System.arraycopy(plane.a, 0, this.a, 0, plane.a.length); }
|
补充一下System.arraycopy的参数
1
| public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
|
src
:源数组,即要复制数据的数组。
srcPos
:源数组中要开始复制的位置的索引。
dest
:目标数组,即要将数据复制到的数组。
destPos
:目标数组中开始复制的位置的索引。
length
:要复制的数组元素的数量。
方法二:使用序列化和反序列化的方法(略)