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