1. 程式人生 > 實用技巧 >初始設計模式之原型模式

初始設計模式之原型模式

一、原型模式是什麼?

​ 原型模式是一種建立型設計模式,Prototype模式允許一個物件再建立另外一個可定製的物件,根本無需知道任何如何建立的細節,工作原理是:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件拷貝它們自己來實施建立。

​ 它主要面對的問題是:“某些結構複雜的物件”的建立工作;由於需求的變化,這些物件經常面臨著劇烈的變化,但是他們卻擁有比較穩定一致的介面。

二、原型模式怎麼用?

淺拷貝

淺拷貝是按位拷貝物件,它會建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值;如果屬性是記憶體地址(引用型別),拷貝的就是記憶體地址 ,因此如果其中一個物件改變了這個地址,就會影響到另一個物件。即預設拷貝建構函式只是對物件進行淺拷貝複製(逐個成員依次拷貝),即只複製物件空間而不復制資源。

class Tag{
	
	private String name;

	Tag(String name){
		this.name = name;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Tag [name=" + name + "]";
	}
	
} 

class Bottle implements Cloneable{
	
	private Tag tag;
	private String color;
	private Double price;
	
	Bottle(Tag tag,String color,Double price){
		this.tag = tag;
		this.color = color;
		this.price = price;
	}
	
	public Tag getTag() {
		return tag;
	}

	public void setTag(Tag tag) {
		this.tag = tag;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Bottle [tag=" + tag + ", color=" + color + ", price=" + price + "]";
	}

	public Object clone() throws CloneNotSupportedException {
        System.out.println("執行拷貝");
        return super.clone();
	}
	
}

public class Application {

	public static void main(String[] args) {
		Tag tag = new Tag("礦泉水");
		Bottle a = new Bottle(tag,"Blue",3.00);
		try {
			Bottle b = (Bottle)a.clone();
			System.out.println("此時不做修改:");
			System.out.println("a:"+a);
			System.out.println("b:"+b);
			b.getTag().setName("純淨水");
			b.setColor("Red");
			System.out.println("此時已經修改了引用型別和基本資料型別:");
			System.out.println("a:"+a);
			System.out.println("b:"+b);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}

}
//執行結果:
/*
 *	 執行拷貝
 *   此時不做修改:
 *   a:Bottle [tag=Tag [name=礦泉水], color=Blue, price=3.0]
 *   b:Bottle [tag=Tag [name=礦泉水], color=Blue, price=3.0]
 *   此時已經修改了引用型別和基本資料型別:
 *   a:Bottle [tag=Tag [name=純淨水], color=Blue, price=3.0]
 *   b:Bottle [tag=Tag [name=純淨水], color=Red, price=3.0]
**/

簡單說明:原型類需要實現Cloneable介面並重寫,clone()方法。我們看以上程式執行結果可以驗證,引用型別Tag類在修改時,原型對應的值也跟隨改變;而拷貝後的物件的基礎資料型別改變後,原型並未跟隨改變。

深拷貝

深拷貝,在拷貝引用型別成員變數時,為引用型別的資料成員另闢了一個獨立的記憶體空間,實現真正內容上的拷貝。

class Tag implements Cloneable{
	
	private String name;

	Tag(String name){
		this.name = name;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Tag [name=" + name + "]";
	}
	
	public Object clone() throws CloneNotSupportedException {
        System.out.println("執行拷貝");
        return super.clone();
	}
	
} 

class Bottle implements Cloneable{
	
	private Tag tag;
	private String color;
	private Double price;
	
	Bottle(Tag tag,String color,Double price){
		this.tag = tag;
		this.color = color;
		this.price = price;
	}
	
	public Tag getTag() {
		return tag;
	}

	public void setTag(Tag tag) {
		this.tag = tag;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Bottle [tag=" + tag + ", color=" + color + ", price=" + price + "]";
	}

	public Object clone() throws CloneNotSupportedException {
        System.out.println("執行拷貝");
        Bottle bottle = (Bottle)super.clone();
        bottle.tag = (Tag)tag.clone();
        return bottle;
	}
	
}

public class Application {

	public static void main(String[] args) throws CloneNotSupportedException {
		Tag tag = new Tag("礦泉水");
		Bottle a = new Bottle(tag,"Blue",3.00);
		Bottle b = (Bottle)a.clone();
		System.out.println("此時不做修改:");
		System.out.println("a:"+a);
		System.out.println("b:"+b);
		b.getTag().setName("純淨水");
		b.setColor("Red");
		System.out.println("此時已經修改了引用型別和基本資料型別:");
		System.out.println("a:"+a);
		System.out.println("b:"+b);
	}

}
/*執行結果:
 *
 *	 執行拷貝
 *   此時不做修改:
 *   a:Bottle [tag=Tag [name=礦泉水], color=Blue, price=3.0]
 *   b:Bottle [tag=Tag [name=礦泉水], color=Blue, price=3.0]
 *   此時已經修改了引用型別和基本資料型別:
 *   a:Bottle [tag=Tag [name=礦泉水], color=Blue, price=3.0]
 *   b:Bottle [tag=Tag [name=純淨水], color=Red, price=3.0]
 *
*/

簡單說明:與淺拷貝相比,引用型別的值並不會影響到其他物件。在程式碼上,則是對引用型別也實現了Cloneable介面,並重寫clone()方法。並在Bottle類的clone()方法裡進行了Tag類的clone()方法的呼叫。關於深拷貝還有其他的實現方法,但在這裡沒有寫出。

三、原型模式再理解

可以考慮使用的場景:

  1. 在需要一個類的大量物件的時候,使用原型模式是最佳選擇,因為原型模式是在記憶體中對這個物件進行拷貝,要比直接new這個物件效能要好很多,在這種情況下,需要的物件越多,原型模式體現出的優點越明顯。
  2. 如果一個物件的初始化需要很多其他物件的資料準備或其他資源的繁瑣計算,那麼可以使用原型模式。
  3. 當需要一個物件的大量公共資訊,少量欄位進行個性化設定的時候,也可以使用原型模式拷貝出現有物件的副本進行加工處理。

以上就是全部內容,如有錯誤,歡迎交流指正。