原型模式(Prototype Pattern)也有人將原型模式稱爲克隆模式,是屬於創造型設計模式,用於建立重複的對象,提供了一種建立對象的最佳方式。原型模式須要實現Cloneable接口,來實現對象的克隆。在實際的應用中,若是應用須要反覆建立相同的對象時,而且建立這個對象須要花費大量時間或者須要訪問權限,好比須要讀取數據庫,配置文件等,若是每次建立重複對象都須要讀一次數據庫,那麼這種方式顯然並非高效的。這時能夠考慮使用原型模式來解決,提升效率,此時只須要在建立原型對象時須要讀取一次數據庫或配置文件等,當後面須要須要建立這個對象時只須要從原型對象克隆一個出來便可。另外,原型模式也解決了構建複雜對象時繁瑣的過程,原型模式不關心對象建立的細節,用戶只須要調用克隆的方法就能夠建立出一個一摸同樣的對象,簡化建立流程。java
既然原型模式也成爲克隆模式,那麼對象複製過程必然用到Java的克隆方法。因此你也須要了解什麼是淺克隆和深克隆。數據庫
淺克隆複製的是對象基本類型的屬性,對於引用類型的屬性,淺克隆置複製該應用類型的地址,由於克隆對象的被克隆對象的應用類型屬性是同一個內存地址,即爲同一個對象,因此在修改其中一個對象的該屬性時,另外一個對象的改屬性也會被修改,很容易將原型對象屬性修改,這也是在使用原型模式時須要注意的地方。淺克隆在代碼中的實現也比較簡單,Java語言中自己就已經提供相關的接口和方法了,咱們在使用時只須要繼承Cloneable接口,重寫clone方法便可實現對象的淺克隆。代碼實現以下:設計模式
public class Sheep implements Cloneable { private String name; private Color color = new Color(); public Sheep() { } public Sheep(String name, String color) { this.name = name; setColor(color); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", color=" + color + '}'; } public Color getColor() { return color; } public void setColor(String color) { this.color.setColor(color); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class Color implements Cloneable { private String color; public Color() { } public Color(String color) { this.color = color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Color{" + "color='" + color + '\'' + '}'; } }
測試ide
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep test = new Sheep("test","白色");
System.out.println(test);
Sheep clone = (Sheep) test.clone();
clone.setColor("黑色");
clone.setName("test01");
System.out.println(test);
System.out.println(clone);
}
}
運行程序時控制檯打印出了:測試
Sheep{name='test', color=Color{color='白色'}}優化
Sheep{name='test', color=Color{color='黑色'}}this
Sheep{name='test01', color=Color{color='黑色'}}設計
很顯然,test對象建立時是白色的,而後用這個對象進行克隆獲得 clone 實例,而後將clone 對象的顏色修改爲黑色,name修改爲test01,最終兩個對象的顏色都變成了黑色,印證了上面說的話,對於引用類型克隆的是對象的內存地址。可能會有人好奇,String也是引用類型,爲何克隆對象修改了name屬性,原型對象卻沒有被修改了?這是由於String是final類型,克隆過程當中天然會是兩個不一樣的內存地址。對象
深克隆和淺克隆的區別在於,深克隆時引用類型屬性複製的是該屬性的值,與原型對象的擁有不一樣的內存地址,即兩個是不一樣的對象,他們任意一個改屬性值都不會影響到彼此。深克隆的實現方式有兩種,第一種,實現Cloneable接口,重寫clone方法,與淺克隆不一樣的是多一步將引用類型的變量再調用一次改變量的clone方法。不推薦用這種方法實現深克隆,每次修改對象的變量時都須要修改一次clone方法,違反了ocp原則。第二種,利用Java序列化與發序列化來實現,推薦使用這種方式。blog
代碼實現:
public class Color implements Cloneable, Serializable { private String color; public Color() { } public Color(String color) { this.color = color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Color{" + "color='" + color + '\'' + '}'; } }
public class Sheep implements Cloneable, Serializable { private String name; private Color color = new Color(); public Sheep() { } public Sheep(String name, String color) { this.name = name; setColor(color); } /** * 利用序列化與反序列化實現深克隆 * @return */ public Object deepClone() { ByteArrayInputStream bis = null; ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ObjectInputStream ois = null; try { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); return ois.readObject(); }catch (Exception e) { e.printStackTrace(); }finally { try { if (ois != null) { ois.close(); } if (bis != null) { bis.close(); } if (oos != null) { oos.close(); } if (bos != null) { bos.close(); } }catch (Exception e) { e.printStackTrace(); } } return null; } @Override protected Object clone() throws CloneNotSupportedException { Sheep clone = (Sheep) super.clone(); clone.color = (Color) clone.color.clone(); return clone; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", color=" + color + '}'; } public Color getColor() { return color; } public void setColor(String color) { this.color.setColor(color); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
注意,若是使用Java的序列化與反序列化,則改對象須要實現Serializable接口,不然會拋序列化異常。
一、原型模式有兩種實現方式,第一種利用Object類中的clone方式,重寫Cloneable的clone方法,淺克隆時直接調用Object類提供的clone方式便可。深克隆則須要再調用須要被克隆的對象的clone方法,固然該對象也必須實現Cloneable接口。第二種方式是利用Java的序列化和反序列化技術,這種方式也有一個缺點是全部須要序列化的變量都必需要實現Serializable接口。
二、原型模式的優勢:提升效率;屏蔽複雜的對象構建過程,簡化代碼。
三、原型模式的缺點:
1)配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
2)必須實現 Cloneable 接口或Serializable接口。
四、原型模式的應用場景:
1)資源優化場景。
2)對象初始化須要大量的資源,包括數據,硬件資源等。