大衛的Design Patterns學習筆記02:Factory

來源:互聯網
上載者:User

 

大衛的Design Patterns學習筆記02:Factory  一、概述Factory(工廠)模式用於封裝對象的建立過程,將對象的建立獨立成單獨的程式模組,從而提高整個應用系統的Flexibility。  二、結構主要有以下三種Factory模式:1、Simple Factory模式:專門定義一個類來負責建立其它類的執行個體,被建立的執行個體通常都具有共同的父類。圖1:Simple Factory的類圖2、Factory Method模式:將對象的建立交由父類中定義的一個標準方法來完成,而不是其建構函式,究竟應該建立何種對象由具體的子類負責決定。圖2:Factory Method的類圖3、Abstract Factory模式:提供一個共同的介面來建立相互關聯的多個對象。圖3:Abstract Factory的類圖 三種Factory模式的區別在於:Simple Factory在於對產品建立過程的簡單封裝,它簡單地根據輸入來決定建立何種產品(這些產品不一定屬於同一產品族),因此,任何產品種類的更新都將對Simple Factory的代碼造成影響;Factory Method面對的是一個產品族,它引入了ConcreteFactory來決定建立產品族中的何種產品,當產品種類增加時,只需建立新的ConcreteFactory來建立新的產品;而Abstract Factory面對的則是多個產品系列,它是Factory Method的延伸,Abstract Factory在一個ConcreteFactory中包含了多個Factory Method,以用於建立多個不同產品族中的多個產品。需要注意的是,以上所說的產品並非僅限於單個的產品,可以包括一次建立出來的一組相同或者相關產品,從這個意義上講,三種Factory特別是Factory Method與Abstract Factory之間的界限並非十分明顯。  三、應用三種Factory模式均用於對象的建立,但Factory Method與Abstract Factory由於引進了一個用於定義介面的基類Factory,可以更好地屏蔽內部的建立過程,因此,可以做到客戶代碼與實現代碼的完全隔離(如樣本部分的COM類廠);而Simple Factory在這一點上表現則不佳,因此,往往仍然只是客戶代碼的一部分,只是將對象建立的工作納入自己管轄之下而已。但並不是說Abstract Factory就比Simple Factory有用,三種Factory模式的實質是一樣的,即用於封裝對象的建立過程,在實際使用中應根據自己的需要加以選擇。  四、優缺點優點:由於對對象建立過程的封裝,不但使得代碼的維護工作變得更加簡單(職責分離了,明確了,找問題變得更加容易),同時也使得應用系統對於新增產品變得不那麼敏感,當新增產品時,我們只需要修改Factory代碼或者擴充新的子類來進行產品的建立,靈活性和可重用性都得到了提高。缺點:由於職責的分離以及類的數目的增多,代碼量的增加往往是不可避免的,同時,隨著產品種類的變更,類層次將不可避免發生幾何膨脹。  五、樣本1、Simple Factory樣本<Java Design Patterns A Tutorial>一書中還給出了一個很巧妙的Simple Factory的應用執行個體。該執行個體如下:一般的英文人名有兩種寫法,"firstname lastname"或者"lastname, firstname",現在我們有一個通訊錄程式需要根據輸入的人名資訊(可能是上面的任意一種形式)分別解析出輸入的firstname和lastname,儲存到通訊錄檔案中。如何解決這個問題呢?由於問題本身比較簡單,我們可以簡單地藉助if等來解決這一問題。但是,如果我們的需求突然發生變化,我們還要支援一些其它的形式,如firstname abbreviation.,甚至更多的情況,這時我們怎麼辦呢?如果我們最初採用的是if等判斷語句,顯然我們需要修改我們解析資料部分的代碼,增加更多的分支,所有的代碼可能需要重新進行嚴格的測試。那麼,有沒有比較OO,比較靈活的解決方案呢?<Java Design Patterns A Tutorial>給了我們如下的解決方案(Java Code): public class Namer {       //base class extended by two child classes       protected String last; //split name       protected String first; //stored here        public String getFirst() {              return first; //return first name       }       public String getLast() {              return last; //return last name       }} public class FirstFirst extends Namer {       //extracts first name from last name       //when separated by a space       public FirstFirst(String s) {              int i = s.lastIndexOf(" "); //find sep space              if (i > 0) {                     first = s.substring(0, i).trim();                     last = s.substring(i + 1).trim();              }              else {                     first = ""; // if no space                     last = s; // put all in last name              }       }} public class LastFirst extends Namer {       // extracts last name from first name       // when separated by a comma       public LastFirst(String s) {              int i = s.indexOf(","); //find comma              if (i > 0) {                     last = s.substring(0, i).trim();                     first = s.substring(i + 1).trim();              }              else {                     last = s; //if no comma,                     first = ""; //put all in last name              }       }} public class NamerFactory {       //Factory decides which class to return based on       //presence of a comma       public Namer getNamer(String entry) {              //comma determines name order              int i = entry.indexOf(",");              if (i > 0)                     return new LastFirst(entry);              else                     return new FirstFirst(entry);       }} 整個實現方案的類圖如下:圖4. Namer, NamerFactory及類圖圖中採用的是與圖1不同的一種結構,是圖1所示的基本的SimpleFactory變形後的結果。當新增需求時,我們要做的是從Namer派生新的輸入格式封裝類,並對NamerFactory中的代碼進行簡單修改,根據輸入建立新的封裝類執行個體。 2、Factory Method樣本應用Factory Method的最典型的例子當數COM的類廠,以下是一段典型的COM建立並運用組件提供的介面方法的代碼:IUnknown *pUnk=NULL;IObject *pObject=NULL;CoInitialize(NULL);CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&pUnk);pUnk->QueryInterface(IID_IOjbect, (void**)&pObject);pUnk->Release();pObject->Func();pObject->Release();CoUninitialize();當我們執行上述操作時,類廠在背後默默地為我們完成了建立組件類對象的工作。下面是CoCreateInstance內部實現的虛擬碼:CoCreateInstance(....){       .......       IClassFactory *pClassFactory=NULL;       CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pClassFactory);       pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);       pClassFactory->Release();       ........}這段話的意思就是先得到類廠對象,再通過類廠建立組件從而得到IUnknown指標。繼續深入一步,看看CoGetClassObject的內部偽碼:CoGetClassObject(.....){       //通過查註冊表CLSID_Object,得知組件DLL的位置、檔案名稱       //裝入DLL庫       //使用函數GetProcAddress(...)得到DLL庫中函數DllGetClassObject的函數指標。       //調用DllGetClassObject}DllGetClassObject是幹什麼的,它是用來獲得類廠對象的,只有先得到類廠才能去建立組件。下面是DllGetClassObject的偽碼:DllGetClassObject(...){       ......       CFactory* pFactory= new CFactory;   //類廠對象       //查詢IClassFactory指標       pFactory->QueryInterface(IID_IClassFactory, (void**)&pClassFactory);       pFactory->Release();       ......}CoGetClassObject的流程已經到此為止,現在返回CoCreateInstance,看看CreateInstance的偽碼:CFactory::CreateInstance(.....){       ...........       CObject *pObject = new CObject;       //組件對象       pObject->QueryInterface(IID_IUnknown, (void**)&pUnk);       pObject->Release();       ...........}以下是上述建立過程的圖示:圖5. CoCreateInstance流程圖關於組件建立流程的更為詳細的論述,請參見<COM技術內幕>。如果沒有類廠的協助,而是將組件類的建立工作交由我們自己完成,則我們必須知道組件類的類名,而如果這樣,則COM的封裝性和可重用性都將大大受損。正是由於運用了Factory Method,COM組件只負責對外提供介面,封裝內部實現,包括建立過程的特性才得以完整體現。 在MFC中,CDocTemplate是另一個典型的應用Factory Method的例子,MFC實現中通過派生不同的子類CSingleDocTemplate/CMultiDocTemplate來支援不同的文件類型,感興趣的朋友可以自己研究CDocTemplate及其子類的相關代碼,相關代碼在afxwin.h中,與上面討論的Factory Method不同的是,這裡的文件類型(單文檔/多文檔)並沒有嚴格一致的類與之對應。 以下是一個簡單的Factory Method的例子(Java Code): // Abstract Product Classabstract class Button {       public String caption;       public abstract void paint();} // ConcreteProduct1class WinButton extends Button {       public void paint() {              System.out.println("I'm a WinButton: " + caption);       }} // ConcreteProduct2class MOTButton extends Button {       public void paint() {              System.out.println("I'm a MOTButton: " + caption);       }} // Abstract Factory Classabstract class GUIFactory {       // Simple Factory Method       public static GUIFactory getFactory(String type) {              if (type.equals("WIN")) {                     return(new WinFactory());              }              else if (type.equals("MOT")) {                     return(new MOTFactory());              }              else                     return null;       }       // factory method       public abstract Button createButton();} // ConcreteFactory1class WinFactory extends GUIFactory {       public Button createButton() {              return(new WinButton());       }} // ConcreteFactory2class MOTFactory extends GUIFactory {       public Button createButton() {              return(new MOTButton());       }} class FactoryMethodTest {       public static void main(String[] args) {              if (args.length != 1 || (!args[0].equals("WIN") && !args[0].equals("MOT")))   {                     System.out.println("Usage: java FactoryMethodTest [WIN|MOT]");                     return;              }                            GUIFactory aFactory = GUIFactory.getFactory(args[0]);              Button aButton = aFactory.createButton();              aButton.caption = "Play";              aButton.paint();       }       //output is       //I'm a WinButton: Play       //or       //I'm a MOTButton: Play} 其中,我們通過從抽象基類GUIFactory派生子類WinFactory/MOTFactory分別建立不同的Button類系中的不同產品WinButton/MOTButton。 3、Abstract Factory樣本前面已經說過,Abstract Factory只是Factory Method的一種擴充,如果將上面例子中抽象工廠類GUIFactory的介面改成:// Abstract Factory Classabstract class GUIFactory {       // Simple Factory Method       public static GUIFactory getFactory(String type) {              ...       }       public abstract Button createButton();       public abstract Button createPanel();       // more factory methods}以對應多個不同的產品類系,並在各子類中實現新增各factory methods,則上面的樣本就變成一個Abstract Factory了。 (附註:也許看完了本系列的下一篇:Builder模式,再回過頭來看這個例子,你會發現如果再增加一個用於組裝的Director類,則上面的例子還可以成為Builder模式的一個應用執行個體。)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.