標籤:設計模式 prototype
What
Prototype:用原型執行個體指定建立對象的種類,並且通過拷貝這些原型建立新的對象。
Why
Prototype適用於在一個類的執行個體有幾種不同的狀態組合的一種時,建立相應的數目的原型並複製她們,要比每次使用合適的狀態建立它們方便一些,或者為了避免建立一個與產品類層次平行的工廠類層次時,要執行個體化一的類在運行時動態指定時。
How
假設如下情境:有一個複雜的報表,建立過程非常複雜,需要把報表發給兩個領導,其中只有報表少了屬性不同,其他屬性相同。
首先討論一下java中的基礎知識
java的clone()方法:
clone方法將對象複製一份並返回給調用者。一般滿足如下條件:
1、對於任何對象a,都有a.clone()!=x,複製對象和來源物件是不同的對象
2、對於任何對象a,都有a.clone().getClass()==a.getClass(),複製對象和來源物件類型一樣
3、如果對象a的equals()方法定義恰當,那麼a.clone().equals(a)成立。
java中對象複製的實現:
可以利用Object類的clone()方法
1、對象類實現Coneable介面
2、覆蓋實現clone()方法
3、調用super.clone(),實現複製。
以上情境實現代碼如下:
public class Product implements Cloneable{ private String name; private int model; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getModel() { return model; } public void setModel(int model) { this.model = model; } @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }}
public class Report implements Cloneable{ private String name; private Product product; public String getName() { return name; } public void setName(String name) { this.name = name; } public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }}
用戶端調用
public class App { public static void main( String[] args ){ Report report=new Report(); Product product=new Product(); product.setModel(1); product.setName("productName"); report.setName("某某產品報表"); report.setProduct(product); System.out.println(report.getName()+"|"+report.getProduct().getName()); Report report1=(Report)report.clone(); report1.setName("某某產品報表組1"); report1.getProduct().setName("產品1"); System.out.println(report1.getName()+"|"+report1.getProduct().getName()); }}
以上代碼類圖如下:
Discuss
深拷貝VS淺拷貝
以上的例子我們的運行結果如下:
某某產品報表|productName某某產品報表組1|產品1
我們在加一行代碼,在最後一行列印report的資訊
System.out.println(report.getName()+"|"+report.getProduct().getName());
我們的運行結果如下:
某某產品報表|productName某某產品報表組1|產品1某某產品報表|產品1
我們可以看出,第二次列印的結果出了問題,report中的產品名字已經被改變了。
在Report中,其中product屬性,我們引用其他對象,在clone方法中,我們的實現只複製了它的引用,只負責了它的指標,沒有複製它所指向的堆空間裡的屬性,所以在這行代碼report1.getProduct().setName("產品1");也改變了report中product屬性中的name屬性的值,這種拷貝是淺拷貝。
我們修改下report的clone()方法,改為如下:
@Override protected Object clone() { Report report; try { report=(Report) super.clone(); report.setProduct((Product)product.clone()); return report; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }
我們再看下運行結果
某某產品報表|productName某某產品報表組1|產品1某某產品報表|productName
這次結果正確了,在以上代碼中,我們對product屬性,也進行了一次clone,所以report1和report的product屬性指向了不同的堆空間,所以我們改變product中的屬性值,不會相互影響,這個拷貝就是深拷貝。