原型模式(六)

原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

原型模式其實就是從一個對象再創建另外一個可定製的對象,而且不需要知道任何的創建細節。

原型模式需要一個原型類,在Java中的Object類中有clone()方法,只需要實現Cloneable接口即可完成原型模式。
下面以寫簡歷的例子來說明原型模式:
簡歷包含姓名,年齡以及工作經驗信息

簡歷對象:

public class Resume implements Cloneable {
    private String name;
    private Integer age;
    private WorkExperience workExperience;

    public Resume(String name) {
        this.name = name;
        this.workExperience = new WorkExperience();
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public WorkExperience getWorkExperience() {
        return workExperience;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.workExperience.setTimeArea(timeArea);
        this.workExperience.setCompany(company);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}' +
                "WorkExperience{" +
                "timeArea='" + workExperience.getTimeArea() + '\'' +
                ", company=" + workExperience.getCompany() +
                '}';
    }
}

工作經驗對象:

public class WorkExperience {

    private String timeArea;
    private String company;

    public String getTimeArea() {
        return timeArea;
    }

    public void setTimeArea(String timeArea) {
        this.timeArea = timeArea;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}

原型模式測試類:

public class PrototypeTest {
    public static void main(String[] args) {
        Resume resume1 = new Resume("nss");
        resume1.setAge(22);
        resume1.setWorkExperience("xx1年-xx1年", "xx1公司");
        try {
            // 只需要調用clone()方法就可以實現新簡歷的生成,並且可以再修改新簡歷的細節
            Resume resume2 = (Resume) resume1.clone();
            // 對象複製後,引用指向新的對象
            System.out.println(resume1.equals(resume2));
            resume2.setAge(23);
            resume2.setWorkExperience("xx2年-xx2年", "xx2公司");
            System.out.println(resume1.toString());
            System.out.println(resume2.toString());
            // resume2更改age後resume1的age不變
            System.out.println(resume1.getAge().equals(resume2.getAge()));
            // resume2更改WorkExperience後resume1的WorkExperience改變
            System.out.println(resume1.getWorkExperience().equals(resume2.getWorkExperience()));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Output:

false
Resume{name='nss', age=22}WorkExperience{timeArea='xx2年-xx2年', company=xx2公司}
Resume{name='nss', age=23}WorkExperience{timeArea='xx2年-xx2年', company=xx2公司}
false
true

 

Object類的clone方法:

Object類的clone()方法用來創建當前對象的淺表副本。該方法創建一個新對象,然後將當前對象的非靜態字段複製到該新對象。如果字段是值類型的,則對該字段進行逐位複製。如果該字段是引用類型,則複製引用但不復制引用的對象。因此,原始對象及其副本引用同一對象。

淺複製和深複製:

淺複製:被複制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。
clone()方法是淺複製,對於值類型沒問題,對於引用類型,就只是複製了引用,對引用對象還是指向了原來的對象。
有時候我們需要把要複製的對象的所有引用對象都複製一變,這種方式叫做深複製。
深複製:深複製把引用對象的變量指向複製過的新對象,而不是原有的被引用的對象。

把上面的代碼改爲深複製,只需要讓WorkExperience實現Cloneable接口並重寫clone方法,再修改一下Resume類的clone方法即可:

WorkExperience改爲: 

public class WorkExperience implements Cloneable {

    private String timeArea;
    private String company;

    public String getTimeArea() {
        return timeArea;
    }

    public void setTimeArea(String timeArea) {
        this.timeArea = timeArea;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Resume類clone方法改爲:

    /**
     * 對clone方法來說如果字段是值類型的,則對該字段進行逐位複製,如果字段是引用類型,則複製引用但不復制引用的對象
     * 因此,原本的對象及其副本引用的是同一對象。所以如果想讓複製的引用類型字段指向新的對象,需要將引用類型對象再複製一份
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Resume resume = (Resume) super.clone();
        resume.workExperience = (WorkExperience) workExperience.clone();
        return resume;
    }

在複製Resume類時,將WorkExperience類也複製一份即可。

再次運行測試類,結果爲:

false
Resume{name='nss', age=22}WorkExperience{timeArea='xx1年-xx1年', company=xx1公司}
Resume{name='nss', age=23}WorkExperience{timeArea='xx2年-xx2年', company=xx2公司}
false
false

總結:

使用原型模式的作用:

使用原型模式可以提高性能,因爲創建對象時,每new一次,就需要執行一次構造函數,如果構造函數的執行時間很長,那麼多次執行這個初始化操作就很低效。

一般在初始化信息不發生變化的情況下,克隆是最好的辦法。這既隱藏了對象創建的細節,又對性能是大大的提高。使用原型模式等於是不用重新初始化對象,而是動態的獲取對象運行時的狀態。

代碼獲取地址:https://gitee.com/nisen6477/design-patterns.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章