原型模式(Prototype Pattern)
原型模式主要用于创建重复的对象,同时又能保证性能,所以是创建型
在使用原型模式时,我们需要首先创建一个原型对象
,再通过复制
这个原型对象,来创建更多
的同类型
的对象。
使用原型复制对象,性能很高
,所以常用于创建大对象
,或者初始化繁琐
的对象:比如游戏里面的地图等等。
适用场景:
一是类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;
二是通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;
三是一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
注意:原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。
UML的相关知识,可以访问我的另外一篇博文
网上的接口继承的方式实现起来较为简单:
通过ClassA
implements
Cloneable
,然后加入clone()方法即可【但是这是 浅拷贝
】
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
测试代码:
// ClassA 实现了 Cloneable 接口
ClassA a = new ClassA();
a.setXXX();
ClassA b = (ClassA) c.clone();
b.setXXX();
以上方法不过多赘述
这篇文章主要通过调用对象,实现浅拷贝和深拷贝。
看完图说理论:
浅Copy是指被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都指向原来的对象。简单点说就是,只复制了引用,而没有复制真正的对象内容。
深Copy是指被复制的对象的所有变量都含有与原来对象相同的值,属性中的对象都指向被复制过的新对象中属性,而不再是原型对象中的属性。简单点说,就是深Copy把所有的对象的引用以及对象都复制了一遍,在堆中是存在两个相互独立的对象,以及属性中的对象也是相互独立的。
说完了浅拷贝
和深拷贝
,正式开始编码,还是拿游戏地图举例子
非常重要
:大家着重看clone()方法
首先实现浅拷贝
(Shallow Copy):
总地图GameMap
代码:
public class ShallowCopyGameMap {
private String tree;
private String mountain;
public String getTree() {
return tree;
}
public void setTree(String tree) {
this.tree = tree;
}
public String getMountain() {
return mountain;
}
public void setMountain(String mountain) {
this.mountain = mountain;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
}
@Override
public String toString() {
return "{" +
"tree='" + tree + '\'' +
", mountain='" + mountain + '\'' +
'}';
}
}
LOL游戏地图LOLGameMap
如下:
/**
* @description:
* 浅拷贝
*/
public class ShallowCopyLOLGameMap implements Cloneable {
private String dragon;
private ShallowCopyGameMap gameMap; // 关联GameMap对象
public ShallowCopyLOLGameMap(String dragon,
ShallowCopyGameMap gameMap) {
this.dragon = dragon;
this.gameMap = gameMap;
}
public String getDragon() {
return dragon;
}
public void setDragon(String dragon) {
this.dragon = dragon;
}
public ShallowCopyGameMap getGameMap() {
return gameMap;
}
public void setGameMap(String tree, String mountain) {
gameMap.setTree(tree);
gameMap.setMountain(mountain);
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "{" +
"dragon='" + dragon + '\'' +
", gameMap=" + gameMap +
'}';
}
}
这两个类基本相似,都实现了Cloneable
接口
接下来进行测试:
public static void main(String[] args) {
System.out.println("浅拷贝测试");
ShallowCopyGameMap gameMap = new ShallowCopyGameMap();
ShallowCopyLOLGameMap lolGameMap1 =
new ShallowCopyLOLGameMap("纳什男爵", gameMap);
lolGameMap1.setGameMap("树","山");
System.out.println("地图1:" + lolGameMap1);
ShallowCopyLOLGameMap lolGameMap2 =
(ShallowCopyLOLGameMap) lolGameMap1.clone();
lolGameMap2.setGameMap("树2", "山2");
System.out.println("地图2:" + lolGameMap2);
ShallowCopyLOLGameMap lolGameMap3 =
(ShallowCopyLOLGameMap) lolGameMap2.clone();
lolGameMap3.setGameMap("树3", "山3");
System.out.println("地图3:" + lolGameMap3);
System.out.println("重新查看前两个地图: ");
System.out.println("地图1:" + lolGameMap1);
System.out.println("地图2:" + lolGameMap2);
}
测试结果:
浅拷贝测试
地图1:{dragon='纳什男爵', gameMap={tree='树', mountain='山'}}
地图2:{dragon='纳什男爵', gameMap={tree='树2', mountain='山2'}}
地图3:{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}
重新查看前两个地图:
地图1:{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}
地图2:{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}
这就是浅拷贝
的测试结果,实际开发中要非常留意这个问题,最好在测试时将之前的对象再进行测试输出一次
。
我这里提供两种方式实现深拷贝:串行化
和非串行化
非串行化方式
实现:(还是通过实现Cloneable接口,注意这里的clone()方法与浅拷贝的区别)
深拷贝(非串行化):总地图GameMap
代码:代码与浅拷贝相同,不过多解释
public class DeepCopyGameMap2 implements Cloneable {
private String tree;
private String mountain;
public String getTree() {
return tree;
}
public void setTree(String tree) {
this.tree = tree;
}
public String getMountain() {
return mountain;
}
public void setMountain(String mountain) {
this.mountain = mountain;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
}
@Override
public String toString() {
return "{" +
"tree='" + tree + '\'' +
", mountain='" + mountain + '\'' +
'}';
}
}
深拷贝(非串行化):LOL游戏地图LOLGameMap
如下:
特别注意
:注意这里的clone()方法与浅拷贝的区别:
/**
* @description:
* 通过转换为二进制进行深拷贝
*/
public class DeepCopyLOLGameMap2 implements Cloneable {
private String dragon;
private DeepCopyGameMap2 deepCopyGameMap;
public DeepCopyLOLGameMap2(String dragon,
DeepCopyGameMap2 deepCopyGameMap) {
this.dragon = dragon;
this.deepCopyGameMap = deepCopyGameMap;
}
public String getDragon() {
return dragon;
}
public void setDragon(String dragon) {
this.dragon = dragon;
}
public DeepCopyGameMap2 getGameMap() {
return deepCopyGameMap;
}
public void setDeepCopyGameMap2(String tree, String mountain) {
deepCopyGameMap.setTree(tree);
deepCopyGameMap.setMountain(mountain);
}
// 非串行化方式, 在LOLGameMap的clone()方法中调用总地图的clone()方法
public DeepCopyLOLGameMap2 clone() {
DeepCopyLOLGameMap2 clone = null;
try {
clone = (DeepCopyLOLGameMap2)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
assert clone != null; // 非空判断,可以忽略
// 深浅拷贝的区别在这里!!!!!
// 使用了GameMap总地图的clone()方法
// GameMap的字段也是浅拷贝中出错变化的地方
clone.deepCopyGameMap =
(DeepCopyGameMap2) deepCopyGameMap.clone();
return clone;
}
@Override
public String toString() {
return "{" +
"dragon='" + dragon + '\'' +
", deepCopyGameMap=" + deepCopyGameMap +
'}';
}
}
深拷贝(非串行化)测试代码:
public static void main(String[] args) {
System.out.println("深拷贝测试---- 非串行化方式");
DeepCopyGameMap2 deepCopyGameMap = new DeepCopyGameMap2();
deepCopyGameMap.setTree("树");
deepCopyGameMap.setMountain("山");
DeepCopyLOLGameMap2 lolGameMap1 =
new DeepCopyLOLGameMap2("纳什男爵",deepCopyGameMap);
lolGameMap1.setDeepCopyGameMap2("树","山");
System.out.println("地图1:" + lolGameMap1);
DeepCopyLOLGameMap2 lolGameMap2 =
(DeepCopyLOLGameMap2) lolGameMap1.clone();
lolGameMap2.setDeepCopyGameMap2("树2", "山2");
System.out.println("地图2:" + lolGameMap2);
DeepCopyLOLGameMap2 lolGameMap3 =
(DeepCopyLOLGameMap2) lolGameMap2.clone();
lolGameMap3.setDeepCopyGameMap2("树3", "山3");
System.out.println("地图3:" + lolGameMap3);
System.out.println("重新查看前两个地图: ");
System.out.println("地图1:" + lolGameMap1);
System.out.println("地图2:" + lolGameMap2);
}
}
测试结果如下:
深拷贝测试---- 非串行化方式
地图1:{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2:{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
地图3:{dragon='纳什男爵', deepCopyGameMap={tree='树3', mountain='山3'}}
重新查看前两个地图:
地图1:{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2:{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
串行化方式
实现:(必须实现Serializable)
深拷贝(串行化):总地图GameMap
代码:
public class DeepCopyGameMap implements Serializable {
private String tree;
private String mountain;
public String getTree() {
return tree;
}
public void setTree(String tree) {
this.tree = tree;
}
public String getMountain() {
return mountain;
}
public void setMountain(String mountain) {
this.mountain = mountain;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
}
@Override
public String toString() {
return "{" +
"tree='" + tree + '\'' +
", mountain='" + mountain + '\'' +
'}';
}
}
深拷贝(串行化):LOL游戏地图LOLGameMap
如下:
/**
* @description:
* 通过串行化方式进行深拷贝
*/
public class DeepCopyLOLGameMap implements Serializable {
private String dragon;
private DeepCopyGameMap deepCopyGameMap;
public DeepCopyLOLGameMap(String dragon,
DeepCopyGameMap deepCopyGameMap) {
this.dragon = dragon;
this.deepCopyGameMap = deepCopyGameMap;
}
public String getDragon() {
return dragon;
}
public void setDragon(String dragon) {
this.dragon = dragon;
}
public DeepCopyGameMap getDeepCopyGameMap() {
return deepCopyGameMap;
}
public void setDeepCopyGameMap(String tree, String mountain) {
deepCopyGameMap.setTree(tree);
deepCopyGameMap.setMountain(mountain);
}
// 串行化方式进行深拷贝
public DeepCopyLOLGameMap clone() {
DeepCopyLOLGameMap clone = null;
try {
//写入当前对象的二进制流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//读出二进制流产生新的对象
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
clone = (DeepCopyLOLGameMap)ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "{" +
"dragon='" + dragon + '\'' +
", deepCopyGameMap=" + deepCopyGameMap +
'}';
}
}
深拷贝(串行化)测试代码:
public static void main(String[] args) throws CloneNotSupportedException {
System.out.println("深拷贝测试--- 串行化方式");
DeepCopyGameMap deepCopyGameMap = new DeepCopyGameMap();
DeepCopyLOLGameMap lolGameMap1 =
new DeepCopyLOLGameMap("纳什男爵", deepCopyGameMap);
lolGameMap1.setDeepCopyGameMap("树","山");
System.out.println("地图1:" + lolGameMap1);
DeepCopyLOLGameMap lolGameMap2 =
(DeepCopyLOLGameMap) lolGameMap1.clone();
lolGameMap2.setDeepCopyGameMap("树2", "山2");
System.out.println("地图2:" + lolGameMap2);
DeepCopyLOLGameMap lolGameMap3 =
(DeepCopyLOLGameMap) lolGameMap2.clone();
lolGameMap3.setDeepCopyGameMap("树3", "山3");
System.out.println("地图3:" + lolGameMap3);
System.out.println("重新查看前两个地图: ");
System.out.println("地图1:" + lolGameMap1);
System.out.println("地图2:" + lolGameMap2);
}
测试结果如下:
深拷贝测试--- 串行化方式
地图1:{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2:{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
地图3:{dragon='纳什男爵', deepCopyGameMap={tree='树3', mountain='山3'}}
重新查看前两个地图:
地图1:{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2:{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
注意LOLGameMap
的clone()
方法
总结一下:
原型模式通过Object的clone()方法实现,由于是内存操作,无视构造方法和访问权限,直接获取新的对象。但对于引用类型,需使用深拷贝,其它浅拷贝即可。
代码已经上传到Git:请点击访问
如果大家对于原型模式还有更多的使用技巧和使用心得,欢迎评论并在评论中分享自己的链接!