C++設計模式之抽象工廠模式

偉創力(世界500強企業),公司有筆記本生產車間、電視機車間、空調車間、電話生產等車間,各生產車間各行其責,生產出不同類型的產品。偉創力不再是生產單一產品的企業,而是生產出多種不同類型的產品,各產品屬於不同產品等級結構中。在設計模式中,也存在一種類似的模式,可以創建一系列產品,這些產品位於不同產品等級結構,產品之間可以沒有任何聯繫,但他們組合起來,可以成爲一個產品族,稱之爲抽象工廠模式。


1、產品等級結構與產品族

    在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法具有唯一性。但是有時候我們希望一個工廠可以提供多個產品對象,而不是單一的產品對象,如一個電器工廠,它可以生產電視機、電冰箱、空調等多種電器,而不是隻生產某一種電器。爲了更好地理解抽象工廠模式,我們先引入兩個概念:

    (1) 產品等級結構產品等級結構即產品的繼承結構,如一個抽象類是電視機,其子類有海爾電視機、TCL電視機,創維電視機。則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。


產品等級結構圖

    (2) 產品族:在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱。海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中,海爾電視機、海爾電冰箱構成了一個產品族


產品族圖

    當系統所提供的工廠生產的具體產品並不是一個簡單的對象,而是多個位於不同產品等級結構、屬於不同類型的具體產品時就可以使用抽象工廠模式。抽象工廠模式是所有形式的工廠模式中最爲抽象和最具一般性的一種形式。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式需要面對多個產品等級結構,一個工廠等級結構可以負責多個不同產品等級結構中的產品對象的創建。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更爲簡單、更有效率。


2、BeyondCompare的設計與實現

    BeyondCompare是一款代碼比較軟件,能夠比較不同版本的代碼之間的差異,類似SVN版本控制器。例如:在版本1基礎之上進行修改升級爲版本2,BeycongComapare能夠對版本1和2代碼進行比較,檢測出哪些地方存在差異。需求:設計一款類似BeyondCompare的軟件,能夠在Windows平臺和Linux平臺下對Cpp格式和Java格式的源代碼進行比較,檢測出差異。

使用簡單工廠模式的實現方式    


代碼格式類型圖


格式工廠圖

    要在Windows和Linux平臺下都能針對Cpp和Java代碼格式文件進行比較,考慮到擴展性,可以定義一個抽象CPP類,Windows平臺下的Cpp格式和Linux平臺下的Cpp格式都繼承於這個抽象Cpp類。 同時定義一個抽象Java類,Windows平臺下的Java格式和Linux平臺下的Java格式都繼承於這個抽象Java類

    代碼格式類型實現如下:

  1. #ifndef _PRODUCT_H_  
  2. #define _PRODUCT_H_  
  3.   
  4. #include <iostream>  
  5. #include <string>  
  6. using namespace std;  
  7.   
  8. //抽象Cpp代碼格式類  
  9. class CppCodeStyle  
  10. {  
  11. public:  
  12.     //虛函數,用於顯示處理的代碼格式  
  13.     virtual void DisplayCodeStyle() = 0;  
  14. };  
  15.   
  16.   
  17. //Windows Cpp代碼格式  
  18. class WindowsCppCodeStyle : public CppCodeStyle  
  19. {  
  20. public:  
  21.     void DisplayCodeStyle()  
  22.     {  
  23.         cout << "我對Windows Cpp文件進行代碼比較" << endl;  
  24.     }  
  25. };  
  26.   
  27.   
  28. //Linux Cpp代碼格式  
  29. class LinuxCppCodeStyle : public CppCodeStyle  
  30. {  
  31. public:  
  32.     void DisplayCodeStyle()  
  33.     {  
  34.         cout << "我對Linux Cpp文件進行代碼比較" << endl;  
  35.     }  
  36. };  
  37.   
  38. /*********************************************************/  
  39. /*********************************************************/  
  40.   
  41. //抽象Java代碼格式  
  42. class JavaCodeStyle  
  43. {  
  44. public:  
  45.     //虛函數,用於顯示處理的代碼格式  
  46.     virtual void DisplayCodeStyle() = 0;  
  47. };  
  48.   
  49.   
  50. //Windows Java代碼格式  
  51. class WindowsJavaCodeStyle : public JavaCodeStyle  
  52. {  
  53. public:  
  54.     void DisplayCodeStyle()  
  55.     {  
  56.         cout << "我對Windows Java文件進行代碼比較" << endl;  
  57.     }  
  58. };  
  59.   
  60.   
  61. //Linux Java代碼格式  
  62. class LinuxJavaCodeStyle : public JavaCodeStyle  
  63. {  
  64. public:  
  65.     void DisplayCodeStyle()  
  66.     {  
  67.         cout << "我對Linux Java文件進行代碼比較" << endl;  
  68.     }  
  69. };  
  70.   
  71. #endif  

    Cpp格式和Java格式是兩種不同類型的產品,因此可以提供兩個簡單工廠,一個工廠用於創建不同平臺下的Cpp格式對象,另一個工廠用於創建不同平臺下的Java格式對象。

    格式工廠實現代碼如下:

  1. #ifndef _PRODUCT_FACTORY_H_  
  2. #define _PRODUCT_FACTORY_H_  
  3.   
  4. #include "Product.h"  
  5.   
  6. //Cpp格式工廠  
  7. class CppProductFactory  
  8. {  
  9. public:  
  10.     //創建具體平臺的Cpp,strCppType表示windows還是linux下的cpp  
  11.     static CppCodeStyle * CreateCpp(string strCppType)  
  12.     {  
  13.         CppCodeStyle * pCppCodeStyle = NULL;  
  14.   
  15.         if( 0 == strcmp(strCppType.c_str(), "Windows Cpp") )  
  16.         {  
  17.             pCppCodeStyle = new WindowsCppCodeStyle();  
  18.         }  
  19.         else if( 0 == strcmp(strCppType.c_str(), "Linux Cpp") )  
  20.         {  
  21.             pCppCodeStyle = new LinuxCppCodeStyle();  
  22.         }  
  23.         else  
  24.         {  
  25.             return NULL;  
  26.         }  
  27.         return pCppCodeStyle;  
  28.     }  
  29. };  
  30.   
  31.   
  32. //Java格式工廠  
  33. class JavaProductFactory  
  34. {  
  35. public:  
  36.     //創建具體平臺的Java,strJavaType表示windows還是linux下的Java  
  37.     static JavaCodeStyle * CreateJava(string strJavaType)  
  38.     {  
  39.         JavaCodeStyle * pJavaCodeStyle = NULL;  
  40.           
  41.         if( 0 == strcmp(strJavaType.c_str(), "Windows Java") )  
  42.         {  
  43.             pJavaCodeStyle = new WindowsJavaCodeStyle();  
  44.         }  
  45.         else if( 0 == strcmp(strJavaType.c_str(), "Linux Java") )  
  46.         {  
  47.             pJavaCodeStyle = new LinuxJavaCodeStyle();  
  48.         }  
  49.         else  
  50.         {  
  51.             return NULL;  
  52.         }  
  53.         return pJavaCodeStyle;  
  54.     }  
  55. };  
  56.   
  57. #endif  
    CppProductFactory產品工廠用於創建windows平臺和Linux平臺下的Cpp格式對象,JavaProductFactory產品工廠用於創建windows平臺和Linux平臺下的Java格式對象。

    測試文件實現代碼如下:

  1. #include <iostream>  
  2. using namespace std;  
  3. #include "Product.h"  
  4. #include "ProductFactory.h"  
  5.   
  6. int main()  
  7. {  
  8.     /*************創建Windows Cpp *************************************/  
  9.     CppCodeStyle * pWindowsCppCodeStyle = CppProductFactory::CreateCpp("Windows Cpp");  
  10.     pWindowsCppCodeStyle->DisplayCodeStyle();  
  11.   
  12.     /*************創建Linux Java *************************************/  
  13.     JavaCodeStyle * pLinuxJavaCodeStyle = JavaProductFactory::CreateJava("Linux Java");  
  14.     pLinuxJavaCodeStyle->DisplayCodeStyle();  
  15.   
  16.     /*************銷燬操作********************************************/  
  17.     delete pWindowsCppCodeStyle;  
  18.     pWindowsCppCodeStyle = NULL;  
  19.     delete pLinuxJavaCodeStyle;  
  20.     pLinuxJavaCodeStyle = NULL;  
  21.   
  22.     return 0;  
  23. }  

    編譯並執行,結果如下:


    使用簡單工廠模式基本上能夠實現上述的需求,但從程序執行結果來看:"我對Linux Cpp文件進行代碼比較和"我對Linux Java文件進行代碼比較",顯然是不合理的。既然是一款BeyongdCompare代碼比較軟件,安裝在Windows下就只能對Windows下的Cpp和Java代碼進行比較,而不能對Linux下的代碼格式文件進行比較。從程序執行結果來看,既能對Windows下的Cpp文件進行比較,同時又能對Linux的Java文件進行比較,那這個軟件到底安裝在哪個平臺上呢?顯然結果是不正確的。程序執行結果表示處理(Windows或者Linux)下的cpp格式和java格式是沒有關聯的。但實際上(Windows或者Linux)下的Cpp格式和Java格式是有關聯的。兩者組合在一起,表示能夠對Windows下的Cpp和Java文件進行處理,或者表示能夠對Linux下的Cpp和Java文件進行處理。那如何才能使Cpp格式產品和Java格式產品在某一個平臺上進行關聯呢? ------抽象工廠模式可以解決這個問題。


3、抽象工廠模式概述

    抽象工廠模式爲創建一組對象提供了一種解決方案。與工廠方法模式相比,抽象工廠模式中的具體工廠不只是創建一種產品,它負責創建一族產品。抽象工廠模式定義如下:

    抽象工廠模式(Abstract Factory Pattern):提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。它是一種對象創建型模式。

    在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用於產生多種不同類型的產品,這些產品可以沒有任何的聯繫,且位於不同的產品等級中,但這些產品可以組合起來,構成了一個產品族。

抽象工廠模式結構圖

    在抽象工廠模式結構圖中包含如下幾個角色:

    AbstractFactory(抽象工廠):它聲明瞭一組用於創建一族產品的工廠方法,每一個工廠方法對應一種產品。這些產品可以沒有任何的聯繫,但這些產品可以組合起來,可以構成一個產品族。

    ConcreteFactory(具體工廠):它實現了在抽象工廠中聲明的創建產品的工廠方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中。

    AbstractProduct(抽象產品):它爲每種產品聲明接口,在抽象產品中聲明瞭產品所具有的業務方法。

    ConcreteProduct(具體產品):它定義具體工廠生產的具體產品對象,實現抽象產品接口中聲明的業務方法。


4、使用抽象工廠實現BeyongdCompare



代碼格式類型圖


格式工廠圖

    要在Windows和Linux平臺下都能對Cpp和Java代碼格式文件進行比較,考慮到擴展性,可以定義一個抽象CPP類,Windows平臺下的Cpp格式和Linux平臺下的Cpp格式都繼承於這個抽象Cpp類。 同時定義一個抽象Java類,Windows平臺下的Java格式和Linux平臺下的Java格式都繼承於這個抽象Java類。
    定義一個抽象格式工廠,Windows格式工廠繼承於抽象格式工廠,用於創建Windows下的Cpp和Java格式對象。 Linux格式工廠也繼承於抽象格式工廠,用於創建Linux下的Cpp和Java格式對象。
    代碼格式類型實現如下:
  1. #ifndef _PRODUCT_H_  
  2. #define _PRODUCT_H_  
  3. #include <iostream>  
  4. #include <string>  
  5. using namespace std;  
  6.   
  7. //抽象Cpp代碼格式類  
  8. class CppCodeStyle  
  9. {  
  10. public:  
  11.     //虛函數,用於顯示處理的代碼格式  
  12.     virtual void DisplayCodeStyle() = 0;  
  13. };  
  14.   
  15.   
  16. //Windows Cpp代碼格式  
  17. class WindowsCppCodeStyle : public CppCodeStyle  
  18. {  
  19. public:  
  20.     void DisplayCodeStyle()  
  21.     {  
  22.         cout << "我對Windows Cpp文件進行代碼比較" << endl;  
  23.     }  
  24. };  
  25.   
  26.   
  27. //Linux Cpp代碼格式  
  28. class LinuxCppCodeStyle : public CppCodeStyle  
  29. {  
  30. public:  
  31.     void DisplayCodeStyle()  
  32.     {  
  33.         cout << "我對Linux Cpp文件進行代碼比較" << endl;  
  34.     }  
  35. };  
  36.   
  37. /*********************************************************/  
  38. /*********************************************************/  
  39.   
  40. //抽象Java代碼格式  
  41. class JavaCodeStyle  
  42. {  
  43. public:  
  44.     //虛函數,用於顯示處理的代碼格式  
  45.     virtual void DisplayCodeStyle() = 0;  
  46. };  
  47.   
  48.   
  49. //Windows Java代碼格式  
  50. class WindowsJavaCodeStyle : public JavaCodeStyle  
  51. {  
  52. public:  
  53.     void DisplayCodeStyle()  
  54.     {  
  55.         cout << "我對Windows Java文件進行代碼比較" << endl;  
  56.     }  
  57. };  
  58.   
  59.   
  60. //Linux Java代碼格式  
  61. class LinuxJavaCodeStyle : public JavaCodeStyle  
  62. {  
  63. public:  
  64.     void DisplayCodeStyle()  
  65.     {  
  66.         cout << "我對Linux Java文件進行代碼比較" << endl;  
  67.     }  
  68. };  
  69.   
  70. #endif  
    BeyondCompare軟件能運行在Windows平臺和Linux平臺,每個平臺都能對Cpp文件和Java文件進行比較。因此可以創建一個Windows工廠,用於創建Windows下的Cpp對象和Java對象。創建一個Linux工廠,用於創建Linux下的Cpp對象和Java對象。這樣Cpp對象和Java對象就被限制在了某個平臺下。即Cpp對象和Java對象要麼同屬於Windows,要麼同屬於Linux。而不會出現使用簡單工廠模式出現的情況:"Cpp屬於Windows平臺,而Java屬於Linux平臺"。
    格式工廠實現代碼如下:
  1. #ifndef _PRODUCT_FACTORY_H_  
  2. #define _PRODUCT_FACTORY_H_  
  3. #include "Product.h"  
  4.   
  5. //抽象代碼格式工廠  
  6. class CodeStyleFactory  
  7. {  
  8. public:  
  9.     //工廠方法,創建具體的Cpp格式對象  
  10.     virtual CppCodeStyle * CreateCpp() = 0;  
  11.   
  12.     //工廠方法,創建具體的Java格式對象  
  13.     virtual JavaCodeStyle * CreateJava() = 0;  
  14. };  
  15.   
  16.   
  17. //Windows代碼格式工廠  
  18. class WindowsCodeStyleFactory : public CodeStyleFactory  
  19. {  
  20. public:  
  21.     //工廠方法,創建具體的Cpp格式對象  
  22.     CppCodeStyle * CreateCpp()  
  23.     {  
  24.         CppCodeStyle * pCppCodeStyle = new WindowsCppCodeStyle();  
  25.   
  26.         return pCppCodeStyle;  
  27.     }  
  28.   
  29.     //工廠方法,創建具體的Java格式對象  
  30.     JavaCodeStyle * CreateJava()  
  31.     {  
  32.         JavaCodeStyle * pJavaCodeStyle = new WindowsJavaCodeStyle();  
  33.   
  34.         return pJavaCodeStyle;  
  35.     }  
  36. };  
  37.   
  38.   
  39. //Linux代碼格式工廠  
  40. class LinuxCodeStyleFactory : public CodeStyleFactory  
  41. {  
  42. public:  
  43.     //工廠方法,創建具體的Cpp格式對象  
  44.     CppCodeStyle * CreateCpp()  
  45.     {  
  46.         CppCodeStyle * pCppCodeStyle = new LinuxCppCodeStyle();  
  47.   
  48.         return pCppCodeStyle;  
  49.     }  
  50.   
  51.     //工廠方法,創建具體的Java格式對象  
  52.     JavaCodeStyle * CreateJava()  
  53.     {  
  54.         JavaCodeStyle * pJavaCodeStyle = new LinuxJavaCodeStyle();  
  55.           
  56.         return pJavaCodeStyle;  
  57.     }  
  58. };  
  59.   
  60. #endif  
    測試文件實現代碼如下:
  1. #include <iostream>  
  2. using namespace std;  
  3. #include "Product.h"  
  4. #include "ProductFactory.h"  
  5.   
  6. int main()  
  7. {  
  8.     /*************創建Windows Cpp ************************************/  
  9.     CodeStyleFactory * pWindowsCodeStyleFactory = new WindowsCodeStyleFactory();  
  10.     CppCodeStyle * pWindowsCppCodeStyle = pWindowsCodeStyleFactory->CreateCpp();  
  11.     pWindowsCppCodeStyle->DisplayCodeStyle();  
  12.   
  13.     /*************創建Windows Java ************************************/  
  14.     JavaCodeStyle * pWindowsJavaCodeStyle = pWindowsCodeStyleFactory->CreateJava();  
  15.     pWindowsJavaCodeStyle->DisplayCodeStyle();  
  16.       
  17.     cout << "***********************************" << endl;  
  18.   
  19.     /*************創建Linux Cpp ************************************/  
  20.     CodeStyleFactory * pLinuxCodeStyleFactory = new LinuxCodeStyleFactory();  
  21.     CppCodeStyle * pLinuxCppCodeStyle = pLinuxCodeStyleFactory->CreateCpp();  
  22.     pLinuxCppCodeStyle->DisplayCodeStyle();  
  23.       
  24.     /*************創建Linux Java ************************************/  
  25.     JavaCodeStyle * pLinuxJavaCodeStyle = pLinuxCodeStyleFactory->CreateJava();  
  26.     pLinuxJavaCodeStyle->DisplayCodeStyle();  
  27.   
  28.     /*************銷燬Windows相關對象操作****************************/  
  29.     delete pWindowsCodeStyleFactory;  
  30.     pWindowsCodeStyleFactory = NULL;  
  31.     delete pWindowsCppCodeStyle;  
  32.     pWindowsCppCodeStyle = NULL;  
  33.     delete pWindowsJavaCodeStyle;  
  34.     pWindowsJavaCodeStyle = NULL;  
  35.   
  36.     /*************銷燬Linux相關對象操作****************************/  
  37.     delete pLinuxCodeStyleFactory;  
  38.     pLinuxCodeStyleFactory = NULL;  
  39.     delete pLinuxCppCodeStyle;  
  40.     pLinuxCppCodeStyle = NULL;  
  41.     delete pLinuxJavaCodeStyle;  
  42.     pLinuxJavaCodeStyle = NULL;  
  43.   
  44.     return 0;  
  45. }  
    編譯並執行,程序結果如下:


5、抽象工廠模式總結

       簡單工廠模式和工廠方法模式都只產生一種類型的產品對象。然而在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用於產生多種不同類型的產品,這些產品可以沒有任何的聯繫,位於不同的產品等級,但這些產品可以組合起來,構成一個產品族。抽象工廠模式是工廠方法模式的進一步延伸,由於它提供了功能更爲強大的工廠類並且具備較好的可擴展性,在軟件開發中得以廣泛應用。使用抽象工廠模式來實現在不同的操作系統中應用程序呈現與所在操作系統一致的外觀界面。

1.主要優點

    抽象工廠模式的主要優點如下:

    (1) 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被創建。由於這種隔離,更換一個具體工廠就變得相對容易,所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行爲。抽象工廠封裝了變化,封裝了對象創建的具體細節,對客戶端隱藏對象創建的具體細節,符合"封裝變化原則"。

    (2) 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。

    (3) 增加新的產品族很方便,無須修改已有系統,符合“開閉原則”。

    (4) 客戶端可以針對抽象進行編程,而不需要知道具體類型,符合"針對接口進行編程而不是針對具體進行編程原則"。

2.主要缺點

    抽象工廠模式的主要缺點如下:

    增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層代碼,這顯然會帶來較大的不便,違背了“開閉原則"

3、抽象工廠模式具體應用

    (1)根據項目的需要,可以使用Sqlserver、Access、Mysql等數據庫存儲一些對象。例如用任意一種數據庫存儲部門表、員工表。這是抽象工廠模式的一種應用,可以隨時切換數據庫進行存儲。

    (2)QQ空間背景樣式,博客背景樣式等都提供了各種風格的樣式。在Windows平臺和Linux平臺下會有不同的顯示方式。

    (3)開發一個可以在Windows、Linux、Android平臺下運行的圖片閱讀器,不同平臺下有不同的顯示方式。

    (4)網頁下載工具的開發: 根據需要可以下載新浪網頁、騰訊網頁、搜狐網頁等。而在不同平臺下,下載方式可能會不相同。

    (5)淘寶購物最後一個支付環節,可以選擇貨到付款、網上銀行、支付寶等類型支付。在不同平臺上,各種支付的方式可能在操作上存在些差異。

    (6)開發一款可以運行在Windows和Andorid平臺的射擊遊戲,Windows下有手槍、AK47、步槍、大刀等武器,Linux同樣也存在這些武器。

    (7)開發火車票圖像識別軟件(OCR),可以在Windows平臺和Linux平臺運行。對識別的結果可以保存爲txt、word、pdf等格式。

    (8)STL裏面的集合容器List、Vector、Deque等,不管使用哪種類型的容器,都可以使用迭代進行抽象操作。 

    (9)生活中也有很多類似的工廠: 偉創力各個生產車間生產不同的產品; 生產海爾冰箱、海爾空調這些產品;肯德基麥當勞在全球設置各個分工廠;中國石油、紫金礦業、景德鎮陶瓷、中國移動、伊利蒙牛在全國的各個分工廠;順豐快遞、申通快遞在全國的驛站等。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章