設計模式學習--Prototype

What

Prototype:用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。java

Why

Prototype適用於在一個類的實例有幾種不一樣的狀態組合的一種時,創建相應的數目的原型並克隆她們,要比每次使用合適的狀態建立它們方便一些,或者爲了不建立一個與產品類層次平行的工廠類層次時,要實例化一的類在運行時動態指定時。ide

How

假設以下場景:有一個複雜的報表,建立過程很是複雜,須要把報表發給兩個領導,其中只有報表少了屬性不一樣,其餘屬性相同。
首先討論一下java中的基礎知識
java的clone()方法:
clone方法將對象複製一份並返回給調用者。通常知足以下條件:
一、對於任何對象a,都有a.clone()!=x,克隆對象和源對象是不一樣的對象
二、對於任何對象a,都有a.clone().getClass()==a.getClass(),克隆對象和源對象類型同樣
三、若是對象a的equals()方法定義恰當,那麼a.clone().equals(a)成立。
java中對象克隆的實現:
能夠利用Object類的clone()方法
一、對象類實現Coneable接口
二、覆蓋實現clone()方法
三、調用super.clone(),實現克隆。
以上場景實現代碼以下:this

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;
    }
}

客戶端調用spa

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淺拷貝
以上的例子咱們的運行結果以下:code

某某產品報表|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()方法,改成以下:get

@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中的屬性值,不會相互影響,這個拷貝就是深拷貝。

相關文章
相關標籤/搜索