原型模式(Prototype Pattern)定義:用一個已經建立的實例做爲原型,經過複製該原型對象來建立一個和原型相同或類似的新對象java
在《英雄聯盟》這款遊戲裏,有不少小兵。咱們定義小兵類:markdown
// Minion.java
public class Minion{
/**
* 小兵類型:近戰小兵MeleeMinion、遠程小兵CasterMinion、攻城小兵SiegeMinion、超級兵SuperMinion
*/
private String type;
/**
* 顏色
*/
private String color;
/**
* 血量
*/
private int hp;
/**
* 武器
*/
private Weapon weapon;
public Minion(String type, String color, int hp) {
System.out.println("開始構造小兵");
this.type = type;
this.color = color;
this.hp = hp;
}
}
複製代碼
咱們建立一個小兵,只須要這樣Minion minion = new Minion("Melee", "Blue", 200);
app
在每一波兵中,都有五個這樣小兵,咱們就須要new 五個對象。若是這個構造方法是一個很是複雜或耗性能的操做,那咱們建立五個對象過程當中,就消耗大量系統資源。ide
在這種場景中,就很是適合原型模式:用一個已經建立的小兵做爲原型,經過複製該小兵來建立和這個小兵相同或類似的其餘小兵。函數
原型模式的核心就是經過複製現有的實例建立新的實例。在Java的Object類中,clone()方法爲咱們提供了複製對象的功能。性能
protected native Object clone() throws CloneNotSupportedException;
複製代碼
具體的實現方式,只須要在類中實現Cloneable接口(若類沒有實現Cloneable接口,調用clone方法將拋出CloneNotSupportedException異常)。ui
// 小兵類,實現Cloneable接口
public class Minion implements Cloneable{
/**
* 小兵類型:近戰小兵MeleeMinion、遠程小兵CasterMinion、攻城小兵SiegeMinion、超級兵SuperMinion
*/
private String type;
/**
* 顏色
*/
private String color;
/**
* 血量
*/
private int hp;
public Minion(String type, String color, int hp) {
System.out.println("開始構造小兵");
this.type = type;
this.color = color;
this.hp = hp;
}
@Override
public Minion clone() {
try {
// 調用Object類clone方法便可
Minion copy = (Minion) super.clone();
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void display() {
System.out.println("我是" + type + "小兵" + ";顏色:" + color + ";血量:" + hp);
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public static void main(String[] args) throws Exception{
Minion minion = new Minion("Melee", "Blue", 200);
Minion minionCopy = minion.clone();
System.out.println("minion == minionCopy:" + (minion == minionCopy));
minionCopy.display();
}
}
// 輸出結果:
開始構造小兵
minion == minionCopy:false
我是Melee小兵;顏色:Blue;血量:200
複製代碼
從輸出結果咱們能夠看出:this
拷貝對象(clone),能夠分爲兩種拷貝方式:lua
淺拷貝:拷貝對象的引用,而不是copy對象自己url
深拷貝:拷貝對象的自己,即會建立一個新的對象
java提供的clone方法,實現的是淺拷貝。咱們能夠實際驗證一下:
仍是剛纔的Minion類,如今給小兵加一個武器Weapon
// 武器類
public class Weapon implements Cloneable{
/**
* 武器類型
*/
private String type;
/**
* 武器是否損壞
*/
private boolean isDamage;
public Weapon(String type) {
this.type = type;
isDamage = false;
}
/**
* 武器損壞
*/
public void destroy() {
this.isDamage = true;
}
/**
* 武器是否損壞
* @return
*/
public boolean isDamage() {
return isDamage;
}
}
// 小兵類
public class Minion implements Cloneable{
/**
* 武器
*/
private Weapon weapon;
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public static void main(String[] args) throws Exception{
Minion minion = new Minion("Melee", "Blue", 200);
Weapon weapon = new Weapon("刀");
minion.setWeapon(weapon);
Minion minionCopy = minion.clone();
System.out.println("複製小兵1武器是否損壞:" + minionCopy.getWeapon().isDamage());
// 原始小兵武器損壞
minion.getWeapon().destroy();
System.out.println("複製小兵1武器是否損壞:" + minionCopy.getWeapon().isDamage());
}
}
// 輸出結果
開始構造小兵
複製小兵1武器是否損壞:false
複製小兵1武器是否損壞:true
複製代碼
在main方法中代碼中,咱們只讓原始小兵武器損壞,但從輸出結果來看,咱們複製小兵的武器也壞了。這就印證了咱們上面的結論:java提供的clone方法,實現的是淺拷貝。
其實很簡單,咱們能夠手動建立須要深拷貝的對象
public Minion clone() {
try {
Minion copy = (Minion) super.clone();
// 手動建立對象
Weapon weapon = new Weapon(this.getWeapon().getType());
copy.setWeapon(weapon);
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
複製代碼
這種手動建立的方式違背了咱們克隆對象的初衷,咱們能夠改爲:對須要拷貝的成員對象再次克隆
// 武器對象實現Cloneable接口
public class Weapon implements Cloneable{
@Override
public Weapon clone() {
try {
return (Weapon) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
// 小兵類的clone方法
@Override
public Minion clone() {
try {
Minion copy = (Minion) super.clone();
// 克隆對象
Weapon weapon = this.weapon.clone();
copy.setWeapon(weapon);
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
複製代碼
這種克隆對象方式,有時實現起來會特別複雜,好比有大量的非基本數據類型的成員對象;成員對象的引用嵌套很是深,須要對每層都實現clone方法。
更通用的實現深拷貝的方式:經過序列化和反序列化的方式實現對象的拷貝
Java序列化是指將Java對象轉爲字節序列的過程,反序列化爲把字節序列恢復爲Java對象的過程。
具體代碼:
@Override
public Minion clone() {
// 序列化、反序列化方式
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Minion) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
複製代碼
!!! 將要序列化的對象及其成員變量必定要實現Serializable接口,不然序列化時將拋出ava.io.NotSerializableException異常。