大话设计模式—原型模式
原型模式(Prototype Pattern),用原型實例指定創建對象的種類,并通過拷貝這些原型創建新的對象;即用于創建重復的對象,同時又能保證性能。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式是實現了一個原型接口,該接口用于創建當前對象的克隆。當直接創建對象的代價比較大時,則采用這種模式。例如,一個對象需要在一個高代價的數據庫操作之后被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。
在運行期建立和刪除原型;利用一個已有的原型對象,快速的生成和原型對象一樣的實例;
使用場景:
1、當一個系統應該獨立于它的產品創建,構成和表示時。
2、當要實例化的類是在運行時刻指定時,例如,通過動態裝載。
3、為了避免創建一個與產品類層次平行的工廠類層次時。
4、當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型并克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
實例解析:
原型類以及具體原型實現類的代碼在這里就不在列出了,詳情請看上面的類圖;
創建一個類,從數據庫獲取實體類,并把它們存儲在一個 Hashtable 中。
ShapeCache.java
import java.util.Hashtable;public class ShapeCache {private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();public static Shape getShape(String shapeId) {Shape cachedShape = shapeMap.get(shapeId);//返回一個shapeId對應的Shape的克隆對象,注意Shape類要實現Cloneable接口return (Shape) cachedShape.clone();}// 簡化操作:不查數據庫了,直接后臺添加三種形狀public static void loadCache() {Circle circle = new Circle();circle.setId("1");shapeMap.put(circle.getId(),circle);Square square = new Square();square.setId("2");shapeMap.put(square.getId(),square);Rectangle rectangle = new Rectangle();rectangle.setId("3");shapeMap.put(rectangle.getId(),rectangle);} }PrototypePatternDemo 使用 ShapeCache 類來獲取存儲在 Hashtable 中的形狀的克隆。
PrototypePatternDemo.java
public class PrototypePatternDemo {public static void main(String[] args) {ShapeCache.loadCache();Shape clonedShape = (Shape) ShapeCache.getShape("1");System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2");System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3");System.out.println("Shape : " + clonedShape3.getType()); } }輸出結果:
Shape : Circle Shape : Square Shape : Rectangle實現的關鍵:
1、實現克隆操作,在 JAVA 繼承 Cloneable,重寫 clone() 方法來實現對象的淺復制或通過序列化(serializable)的方式來實現深復制。
2、原型模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這些”易變類”擁有穩定的接口。
原型模式的優點:
通過普通的new方式實例化對象時,每new一次就需要執行一次構造函數,如果構造函數的執行時間很長,那么多次執行程序效率就會大打折扣;一般在初始化的信息不發生變化的情況下,克隆是最好的辦法,這既隱藏了對象的創建細節,又大大提升了性能。
淺復制 VS 深復制
以大話設計模式里面的一個經典的簡歷的例子來學習這兩個概念:
淺復制
WorkExperience.java
public class WorkExperience{private string workDate;private string company;public string workDate(){//getter and setter}public string company(){//getter and setter} }Resume.java
public class Resume implements ICloneable{private string name;private string sex;private string age;private WorkExperience work;//引用工作經歷對象//構造函數public Resume(string name){this.name = name;work = new WorkExperience();}//設置個人信息public void setPersonalInfo(String sex,String age){this.sex = sex;this.age = age;}//設置工作經歷public void setWorkExperience(String timeArea,String company){work.timeArea = timeArea;work.company = company;}//顯示public void display(){System.out.println("姓名:" + name + " 性別:" + sex + " 年齡:" + age);”System.out.println("工作經歷:" + work.timeArea +" " + company);}//克隆方法public Object Clone(){return (Object)this.MemberwiseClone();}}測試類:
public class TestPrototype{public satic void main(String[] args){Resume resumeOne = new Resume( "tom"); resumeOne.SetPersonalInfo( "man","22"); resumeOne.SetWorkExperience( "2015-1-2至2015-12-10","IBM"); Resume resumeTwo = (Resume)resumeOne.Clone(); resumeTwo.SetWorkExperience( "2015-1-2至2015-11-11","Microsoft"); Resume resumeThree = (Resume)resumeOne.Clone(); resumeThree.SetPersonalInfo( "man", "32"); resumeThree.SetWorkExperience( "2015-1-2至2015-11-11","甲骨文"); resumeOne.Display(); resumeTwo.Display(); resumeThree.Display(); Console.Read(); } }在你的想象里輸出結果會是什么呢?我們拿事實來說話:
姓名:tom 性別:man 年齡:22 工作經歷:2015-1-2至2015-11-11 甲骨文 姓名:tom 性別:man 年齡:22 工作經歷:2015-1-2至2015-11-11 甲骨文 姓名:tom 性別:man 年齡:32 工作經歷:2015-1-2至2015-11-11 甲骨文看到結果有沒有很驚訝,對于工作經歷的輸出相信出乎了好多人的意料,這個地方我們就要說到MemberwiseClone()這個方法了,(有關MemberwiseClone的詳細內容請參見本人的另一篇文章
深復制VS淺復制(MemberwiseClone方法介紹)
),其對于值類型沒有問題,但是對于引用類型,就只是復制了引用,對引用對象還是指向了原來的對象,所以就會出現我們給resume one two three三處設置工作經歷,但卻得到三個引用都是最后一次的設置,這是因為三個引用都指向了同一對象。
那么問題來了,到底什么是淺復制呢?
淺復制:被復制對象的所有變量都含有與原來對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。
然而我們希望實現的是一變二,二變三,我么叫這種方式為深復制:把引用對象的變量指向復制過的新對象,而不是原來的被引用的對象。(使用深復制要注意避免循環引用的問題)
深復制如何實現這個例子呢?簡歷類和工作經歷類都實現了ICloneable接口;
WorkExperience.java
public class WorkExperience implements ICloneable{private string workDate;private string company;public string workDate(){//getter and setter}public string company(){//getter and setter}//克隆方法public Object Clone(){return (Object)this.MemberwiseClone();} }Resume .java
public class Resume implements ICloneable{private string name;private string sex;private string age;private WorkExperience work;//引用工作經歷對象//構造函數public Resume(string name){this.name = name;work = new WorkExperience();}//注意這個構造方法private Resume(WorkExperience work){this.work = work.(WorkExperience)clone;}//設置個人信息public void setPersonalInfo(String sex,String age){this.sex = sex;this.age = age;}//設置工作經歷public void setWorkExperience(String timeArea,String company){work.timeArea = timeArea;work.company = company;}//顯示public void display(){System.out.println("姓名:" + name + "性別:" + sex + "年齡:" + age);System.out.println("工作經歷:" + work.timeArea + company);}//注意區別該類實現的克隆方法與淺復制時實現的克隆方法的區別public Object Clone(){//調用私有的構造方法克隆工作經歷,然后再給建立對象賦值Resume resume = new Resume(this.work);resume.name = this.name;resume.sex = this.sex;resume.age = this.age;}}測試類同上,這次輸出結果會是什么呢?
姓名:tom 性別:man 年齡:22 工作經歷:2015-1-2至2015-12-10 IBM 姓名:tom 性別:man 年齡:22 工作經歷:2015-1-2至2015-11-11 Microsoft 姓名:tom 性別:man 年齡:32 工作經歷:2015-1-2至2015-11-11 甲骨文由于在一些特定場合,會經常涉及深復制和淺復制,比如說,數據集對象DataSet,他就有Clone()和copy()方法,Clone()方法用來復制DataSet的結構但不復制其數據,實現了原型模式中的淺復制;而copy()方法不但復制其結構還復制其數據,也就是實現了原型模式中的深復制。
總結
以上是生活随笔為你收集整理的大话设计模式—原型模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大话设计模式—单例模式
- 下一篇: 深复制VS浅复制(MemberwiseC