定義:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象 原型模式其實就是通過一個對象來創建一個新的可定製(可以是源對象的一個副本也可以有所改變)的對象,而且我們並不需要知道具體創建的細節。在java中使用原型模式是非常簡單的,因為Object類中提供了一個本地方法clone,就是用來拷 ...
定義:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
原型模式其實就是通過一個對象來創建一個新的可定製(可以是源對象的一個副本也可以有所改變)的對象,而且我們並不需要知道具體創建的細節。在java中使用原型模式是非常簡單的,因為Object類中提供了一個本地方法clone,就是用來拷貝一個對象,當一個類實現了Cloneable介面就能使用該方法,下麵我們來看下原型模式一個簡單的實例
public class Prototype implements Cloneable { protected int numberA; protected int numberB; public Prototype() { this.numberA = 2; this.numberB = 3; } public void changeNumber() { this.numberA = 4; this.numberB = 5; } @Override public Prototype clone() { try { return (Prototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } @Override public String toString() { return "numberA=" + numberA + ", numberB=" + numberB; } }
測試代碼和輸出結果
Prototype prototype = new Prototype(); prototype.changeNumber(); System.out.println(prototype); Prototype copyPrototype = prototype.clone(); System.out.println(copyPrototype);
從上面代碼可以看出我們只是調用了clone方法就成功的創建了一個新的對象,若我們通過new對象的方法想要獲取這樣一個副本,就還得再執行一遍changeNumber方法。而且通過clone創建對象我們不需要知道其中創建的細節,即並有沒有執行構造方法,以上代碼不太好看出來,可以通過在構造方法中加入一條輸出語句來驗證這一點,同時這種方法創建對象的速度要比new對象快
接下來我們來思考一個問題,若原型類中的某個屬性是引用類型,那拷貝出來的新對象中的該屬性是否與源對象中的是指向同一個對象,即地址是否一樣,通過以下的例子就能回答這個問題
public class ShallowPrototype extends Prototype { private Shallow shallow; public Shallow getShallow() { return shallow; } public void setShallow(Shallow shallow) { this.shallow = shallow; } public ShallowPrototype() { super(); Shallow shallow = new Shallow(); shallow.setNumberC(4); this.shallow = shallow; } @Override public void changeNumber() { super.changeNumber(); shallow.setNumberC(6); } @Override public String toString() { return super.toString() + ", shallow=" + shallow; } } class Shallow { private int numberC; public int getNumberC() { return numberC; } public void setNumberC(int numberC) { this.numberC = numberC; } }
其中Prototype即為上面貼出來的類,當我們需要編寫多個類似ShallowPrototype類的時候,通過以上這種繼承的方式,可以讓我們省去重寫clone方法的代碼
測試代碼和輸出結果
ShallowPrototype prototype = new ShallowPrototype(); prototype.changeNumber(); System.out.println(prototype); ShallowPrototype copyPrototype = (ShallowPrototype) prototype.clone(); System.out.println(copyPrototype);
從上面的輸出結果可以看出,兩個對象中的shallow屬性是指向同一個地址,所以若改變其中的一個的nubmerC值,另一個也會發生改變。這種複製為淺表複製,這往往不是我們希望的結果,不過我們可以通過以下的方法來實現深度複製
public class DeepPrototype extends Prototype { private Deep deep; public DeepPrototype() { super(); Deep deep = new Deep(); deep.setNumberC(4); this.deep = deep; } @Override public void changeNumber() { super.changeNumber(); deep.setNumberC(6); } @Override public DeepPrototype clone() { DeepPrototype deepPrototype = (DeepPrototype) super.clone(); deepPrototype.deep = (Deep) deep.clone(); return deepPrototype; } @Override public String toString() { return super.toString() + ", deep=" + deep; } } class Deep implements Cloneable { private int NumberC; public int getNumberC() { return NumberC; } public void setNumberC(int numberC) { NumberC = numberC; } @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
可以看出這與上面淺表複製的區別在與,原型類中的引用類型的屬性也是一個實現了Cloneable的類並重寫了clone方法,而且原型類的clone方法中需要對該屬性進行拷貝
測試代碼的調用和輸出結果
DeepPrototype prototype = new DeepPrototype(); prototype.changeNumber(); System.out.println(prototype); DeepPrototype copyPrototype = prototype.clone(); System.out.println(copyPrototype);
從上面的輸出就能看出深度複製與淺表複製的區別,深度複製出來的新對象,將不會因為源對象的改變而改變。接下來通過深度複製來實現一個複製簡歷的功能,加深一下對原型模式的理解
工作經驗的類(省略了get、set、toString和構造方法)
public class WorkExperience implements Cloneable { private LocalDate startWorkDate; private LocalDate endWorkDate; private String companyName; @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
簡歷類(省略了get、set、toString和構造方法)
public class Resume implements Cloneable { private String name; private String sex; private Integer age; private WorkExperience workExperience; @Override protected Object clone() { try { Resume resume = (Resume) super.clone(); resume.setWorkExperience((WorkExperience) workExperience.clone()); return resume; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
測試代碼和輸出結果
WorkExperience workExperience = new WorkExperience( LocalDate.parse("2018-01-01"), LocalDate.parse("2019-01-01"), "某某公司"); Resume resume = new Resume("張三", "男", 22, workExperience); System.out.println(resume); Resume copyResume = (Resume) resume.clone(); System.out.println(copyResume); resume.getWorkExperience().setEndWorkDate(LocalDate.parse("2020-01-01")); System.out.println(resume); System.out.println(copyResume);
以上就是整個功能的實現代碼,可以通過點擊以下鏈接獲取完整代碼