C++设计模式之抽象工厂模式

来源:互联网 发布:欧洲五大列强知乎 编辑:程序博客网 时间:2024/06/09 21:43

伟创力(世界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类

    代码格式类型实现如下:

[cpp] view plaincopy
  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格式对象。

    格式工厂实现代码如下:

[cpp] view plaincopy
  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格式对象。

    测试文件实现代码如下:

[cpp] view plaincopy
  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格式对象。
    代码格式类型实现如下:
[cpp] view plaincopy
  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平台"。
    格式工厂实现代码如下:
[cpp] view plaincopy
  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  
    测试文件实现代码如下:
[cpp] view plaincopy
  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)生活中也有很多类似的工厂: 伟创力各个生产车间生产不同的产品; 生产海尔冰箱、海尔空调这些产品;肯德基麦当劳在全球设置各个分工厂;中国石油、紫金矿业、景德镇陶瓷、中国移动、伊利蒙牛在全国的各个分工厂;顺丰快递、申通快递在全国的驿站等。



0 0