1. 程式人生 > >5分鐘搞定 java設計模式之原型模式

5分鐘搞定 java設計模式之原型模式

一,引言

    在開發過程中,有時會遇到為一個類建立多個例項的情況,這些例項內部成員往往完全相同或有細微的差異,而且例項的建立開銷比較大或者需要輸入較多引數,如果能通過複製一個已建立的物件例項來重複建立多個相同的物件,這就可以大大減少建立物件的開銷,這個時候就需要原型模式。

02

二,模式詳解

    原型模式可以通過一個物件例項確定建立物件的種類,並且通過拷貝建立新的例項。總得來說,原型模式實際上就是從一個物件建立另一個新的物件,使新的物件有具有原物件的特徵。

    首先,所有可以作為原型的類中都應該有一個用於複製自身的方法clone(),因此,我們可以抽象出一個抽象原型類或介面,該類中只有一個clone(),所有的具體原型類都要實現該方法並定義複製自身的具體行為。在客戶端中通過呼叫具體原型物件的clone方法可以複製原型物件到一個新的物件中。

03

三,具體實現

    原型模式是一種應用及其廣泛的設計模式,Clone也是一種十分常見的操作,以至於在Java中,終極父類Object將Clone方法作為了所有類應具有的基本功能,並且Java也提供了Cloneable介面(關於Cloneable介面的細節,請留意公眾號後續文章“JavaSE學習隨筆之Cloneable介面原始碼分析與技術細節”,這都方便了原型模式的實現。

    我們還是以一個例子來說明原型模式的具體實現和作用。考慮一個寫簡歷的場景,簡歷中包括的資訊有姓名、性別、年齡、家庭成員和工作經驗幾點內容。現在的需求為由於需要向多個公司投遞建立簡歷,因此子建立了一份建立物件之後,還要求能夠對已建立的簡歷進行復制。此時,恰好有幾個另外有幾個同學也想找工作,為了方便,就把已建立的作為模版,然後根據自身的情況作了一些修改。

    為了實現上述要求,我們首先定義一個工作經驗類,裡面有兩個成員變數分別為工作時間和公司名稱。

class WorkExperience {

    public String timeArea = null;

    public String company = null;

}

    接著定義一個簡歷類,該類相當於UML圖中的實體原型類,至於抽像原型類或介面,Java中已經我i我們提供了Cloneable介面,因此我們只需要實現它就可以了(實現的方法可以通過上文給出的連結到我另一篇博文中檢視,在這裡我直接給出了介面的實現),我們首先來看一種實現方式。

class Resume implements Cloneable {

public String name = null;

public Integer age = null;

public String sex = null;

public ArrayList<String> famMem = new ArrayList<>();

public WorkExperience work = null;

public Resume(String name) {

this.name = name;

work = new WorkExperience();

}// Resume

public void setName(String name) {

this.name = name;

}// setName

public void setPersonal(String sex, int age, ArrayList<String> famMem) {

this.age = age;

this.sex = sex;

this.famMem = famMem;

}

// setPersonal

public void setWork(String timeArea, String company) {

work.timeArea = timeArea;

work.company = company;

}// setWork

/** * 重些clone()方法為public型別,並呼叫Object類的本地clone()方法。 */

@Override

public Resume clone() throws CloneNotSupportedException {

return (Resume) super.clone();

}// clone

public void display() {

System.out.println(this.name + " " + this.sex + " " + this.age);

System.out.print("Family member: ");

for (String elem : famMem)

System.out.print(elem + " ");

System.out.println();

System.out.print("Work experience: " + this.work.timeArea);

System.out.println(" " + this.work.company);

}

}

 接下來是測試程式碼:

public class PrototypeDemo {

public static void main(String[] args) throws Exception {

ArrayList<String> famMem = new ArrayList<>(); 

// 家庭成員名單

famMem.add("Papa");

famMem.add("Mama"); // 建立初始簡歷

Resume resume1 = new Resume("Jobs");

resume1.setPersonal("Male", 26,famMem);

resume1.setWork("2013/8/1 - 2015/6/30", "Huawei");

// 通過簡歷1複製出簡歷2,並對家庭成員和工作經驗進行修改

Resume resume2 = resume1.clone();

resume2.setName("Tom");

resume2.famMem.add("Brother");

resume2.setWork("2015/7/1 - 2016/6/30", "Baidu");

resume1.display();

resume2.display();

}

}

測試結果如下:

Jobs Male 26

Family member: Papa Mama Brother 

Work experience: 2015/7/1 - 2016/6/30 Baidu

Tom Male 26

Family member: Papa Mama Brother 

Work experience: 2015/7/1 - 2016/6/30 Baidu

    從執行結果上看,雖然Tom成功複製了Jobs的簡歷,但是隨後對Tom家庭成員和工作經驗的修改卻導致了Jobs的簡歷被同時修改,這是由於我們在實現clone() 方法時直接呼叫了Object類的本地clone()方法造成的,因為Object的clone()方法執行的是淺拷貝,因而Jobs和Tom的簡歷中的famMem和work欄位都指向了同一個物件例項。要想實現深拷貝,就必須要修改clone()方法。

@Override

public Resume clone() throws CloneNotSupportedException {

int age = this.age;

String sex = this.sex;

String name = new String(this.name);

ArrayList<String> famMem = new ArrayList<>(this.famMem);

Resume copy = new Resume(name);

copy.setPersonal(sex, age, famMem);

copy.setWork(this.work.timeArea, this.work.company);

return copy;

}

再進行測試,結果如下:

Jobs Male 26

Family member: Papa Mama 

Work experience: 2013/8/1 - 2015/6/30 Huawei

Tom Male 26

Family member: Papa Mama Brother 

Work experience: 2015/7/1 - 2016/6/30 Baidu

可以看出,使clone()方法具備深拷貝功能後,複製後的建立與原簡歷被獨立開來。

04

四,總結

    原型模式可以說是所有設計模式中最簡單的一個,它沒有複雜的繼承體系,只需要使需要具有拷貝功能的類實現Cloneable介面並重寫clone()方法即可。但它的應用卻及其廣泛,它將對一個物件中各個欄位(不管是私有的還是共有的)的複製操作封裝在了clone()方法中,這樣,使用該類的使用者就不需要對物件中的各個欄位的細節進行了解,直接呼叫clone()方法就可以實現物件的拷貝,而且,通過clone()方法還可以為不同的欄位設定被複制的許可權,從而允許僅對可以被複制的欄位進行復制。

更多幹貨,加小編 微信

tc4534