1. 程式人生 > 實用技巧 >大話設計模式讀書筆記(原型模式)

大話設計模式讀書筆記(原型模式)

人物:小菜,大鳥

事件:小菜正在準備求職見面會,列印了很多簡歷,大鳥讓小菜試著用程式寫簡歷


瞭解原型模型:

1.通過用原始的邏輯實現幾份相同簡歷的列印

2.思考有沒有一種模型可以幫助小菜不用新建物件,直接複製簡歷 --引出原型模型

3.通過試驗值複製和引用複製,知道了深複製和淺複製,然後引出深複製的實現原理和實現內容

小菜簡歷程式碼初步實現

簡歷類:

@Slf4j
public class Resume {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    
private String company; public Resume(String name) { this.name = name; } /** * 設定個人資訊 * * @param sex * @param age */ public void setPersonalInfo(String sex, String age) { this.sex = sex; this.age = age; } /** * 設定工作經歷 * *
@param timeArea * @param company */ public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } /** * 顯示 */ public void display() { log.info("名字:{} 性別:{} 年齡:{}", name, sex, age); log.info(
"工作經歷:{}, {}", timeArea, company); } }

列印三份,客戶端呼叫程式碼:

public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鳥");
        Resume b = new Resume("大鳥");
        Resume c = new Resume("大鳥");
        a.setPersonalInfo("男", "29");
        b.setPersonalInfo("男", "29");
        c.setPersonalInfo("男", "29");
        a.setWorkExperience("2019-2020", "凡人修仙公司");
        b.setWorkExperience("2019-2020", "凡人修仙公司");
        c.setWorkExperience("2019-2020", "凡人修仙公司");
        a.display();
        b.display();
        c.display();
    }
}

大鳥:這樣如果要列印20份,也就要例項20個物件,如果寫好20份後,如果要改下年份,那不是20個要一個一個改麼?你其實可以先這樣改下:

public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鳥");
        a.setPersonalInfo("男", "29");
        a.setWorkExperience("2018-2020", "凡人修仙公司");
        Resume b = a;
        Resume c = a;
        a.display();
        b.display();
        c.display();
    }
}

大鳥:這樣就比剛才好多了,其實是傳引用,不是傳值,就像一份簡歷,每次複製與內容有關,與用什麼紙無關,下面我們就可以開始瞭解原型模型了

簡歷的原型模型實現

原型模型:用原型模型指定建立物件的種類,並通過拷貝這些原型建立新的物件(而且不用知道建立的細節)

簡歷類:

@Slf4j
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;

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

    /**
     * 設定個人資訊
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 設定工作經歷
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    /**
     * 顯示
     */
    public void display() {
        log.info("名字:{} 性別:{} 年齡:{}", name, sex, age);
        log.info("工作經歷:{}, {}", timeArea, company);
    }

    @Override
    public Object clone() {
        Resume resume = null;
        try {
            resume = (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }
}

客戶端呼叫:

@Slf4j
public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鳥");
        a.setPersonalInfo("男", "29");
        a.setWorkExperience("2019-2020", "凡人修仙功法公司");

        Resume b = (Resume) a.clone();
        b.setWorkExperience("2020-2021", "凡人修仙寶器公司");

        Resume c = (Resume) a.clone();
        c.setWorkExperience("2020-2021", "凡人修仙煉體公司");

        a.display();
        b.display();
        c.display();
    }
}

大鳥:對的,這樣實現後,這樣每次複製簡歷或者要修改簡歷,都是直接克隆,而不是再去建立一個新的例項,大大提高了效能,還隱藏了實現的細節

大鳥:但注意了,我們剛才複製的,都是String型別,也就是都是值型別,但如果是引用型別,就只會複製引用,不會複製引用的型別,因此原始物件及其複本引用同一物件

如下:

@Slf4j
@Data
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private WorkExperience work;

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

    /**
     * 設定個人資訊
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 設定工作經歷
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        work.setTimeArea(timeArea);
        work.setCompany(company);
    }

    /**
     * 顯示
     */
    public void display() {
        log.info("名字:{} 性別:{} 年齡:{}", name, sex, age);
        log.info("工作經歷:{}, {}", work.getTimeArea(), work.getCompany());
    }

    @Override
    public Object clone() {
        Resume resume = null;
        try {
            resume = (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }
}

其中:private WorkExperience work是引用工作型別的物件

在簡歷例項化時,同時例項化工作經歷:

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

在設定工作經歷時,給物件的兩屬性值賦值:

/**
 * 設定工作經歷
 *
 * @param timeArea
 * @param company
 */
public void setWorkExperience(String timeArea, String company) {
    work.setTimeArea(timeArea);
    work.setCompany(company);
}

這時如果再執行之前的客戶端程式碼,則都只顯示a物件的資訊

大鳥小結:如果是值物件,直接複製沒有問題,如果是引用型別,只是複製了引用,對引用的物件還是指向了原來的物件,這叫做淺複製。淺複製指被複制物件的所有變數都含有與原來物件相同的值,而所有的對其他物件的引用都仍然指向原來的物件。當我們需要把所有引用的物件都複製一遍時,這就需要深複製,深複製會把引用物件的變數指向複製過的新物件,而不是原有的被引用的物件

深複製:

現在工作經歷類中,複製傳進來的值:

@Data
public class WorkExperience implements Cloneable {
    private String timeArea;
    private String company;

    public Object cloneInfo() {
        Object object = null;
        try {
            object = (Object)this.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return object;
    }
}

然後在簡歷類中,先將傳進來的工作經歷複製,將a裡工作經歷的內容重新賦值後傳了一個新物件給b,b也就得到了改變的值:

@Slf4j
@Data
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private WorkExperience work;

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

    /**
     * 設定個人資訊
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 設定工作經歷
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        work.setTimeArea(timeArea);
        work.setCompany(company);
    }

    /**
     * 顯示
     */
    public void display() {
        log.info("名字:{} 性別:{} 年齡:{}", name, sex, age);
        log.info("工作經歷:{}, {}", work.getTimeArea(), work.getCompany());
    }

    @Override
    public Object clone() {
        Resume obj = new Resume(this.work);
        obj.name = this.name;
        obj.sex = this.sex;
        obj.age = this.age;
        return obj;
    }

    private Resume(WorkExperience work) {
        try {
            this.work = (WorkExperience)work.cloneInfo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

這樣得到的結果就不像淺複製一樣都是一樣的了,而是:

名字:大鳥 性別:男 年齡:29
工作經歷:2019-2020, 凡人修仙功法公司
名字:大鳥 性別:男 年齡:29
工作經歷:2020-2021, 凡人修仙寶器公司
名字:大鳥 性別:男 年齡:29
工作經歷:2020-2021, 凡人修仙煉體公司
名字:大鳥 性別:男 年齡:29