前面说过,简单工厂模式是最基础的一种设计模式,那以工厂命名的设计模式就是23种设计模式中最多的一种,他们一脉相承,一步一步进化而来,这里就是其中的最后一种——抽象工厂模式(Abstract Factory),其是在工厂方法模式的基础上改进而来,如果没有弄明白工厂方法模式的同学请先观看《大话设计模式C++版——工厂方法模式》。
为什么会有抽象工厂模式?抽象工厂模式是简单工厂模式缺陷的终极解决方式么?NO,抽象工厂模式并不是为了解决简单工厂模式的缺陷而活着,它是因为有新的使命而诞生。 一个简单的例子,有一个数据库保存着公司员工的信息,为了能够灵活使用各种数据库,将操作数据库的类抽象出来,并采用比简单工厂模式先进那么一点点的工厂方法模式实现。
1、工厂方法模式实现员工信息数据库操作
1.1 员工类和工厂类
typedef struct Employee { int nID; TString tstrName; }; class IEmployee { public: ~IEmployee() {} virtual bool InserttoDB(Employee& stEmployee) = 0; virtual Employee GetEmployee(int nID) = 0; }; class CEmployeefromMysql : public IEmployee { public: bool InserttoDB(Employee& stEmployee) { _tprintf(_T("Insert employee %s into mysql\n"), stEmployee.tstrName.c_str()); return true; } Employee GetEmployee(int nID) { Employee stEmployee; printf("Get an employee from mysql by id %d\n", nID); return stEmployee; } }; class CEmployeefromAccess : public IEmployee { public: bool InserttoDB(Employee& stEmployee) { _tprintf(_T("Insert employee %s into access\n"), stEmployee.tstrName.c_str()); return true; } Employee GetEmployee(int nID) { Employee stEmployee; printf("Get an employee from access by id %d\n", nID); return stEmployee; } }; class IFactory { public: ~IFactory() {} virtual IEmployee* CreateEmployee() = 0; }; class CFactoryfromMysql : public IFactory { public: IEmployee* CreateEmployee() { return new CEmployeefromMysql(); } }; class CFactoryfromAccess : public IFactory { public: IEmployee* CreateEmployee() { return new CEmployeefromAccess(); } };
分别有 Mysql 和 Access 2种数据库的员工对象类和2种工厂对象类。
1.2 使用示例
void Test() { IFactory* poIFactory = new CFactoryfromMysql(); IEmployee* poIEmployee = NULL; if (!poIFactory) { return; } poIEmployee = poIFactory->CreateEmployee(); if (poIEmployee) { Employee stEmployee; stEmployee.nID = 1; stEmployee.tstrName = _T("Jim"); poIEmployee->InserttoDB(stEmployee); delete poIEmployee; } delete poIFactory; }
注:TString定义如下
#ifdef UNICODE #define TString std::wstring #else #define TString std::string #endif
如果需要更换数据为 Access,只需要将“IFactory* poIFactory = new CFactoryfromMysql();”
改为“IFactory*
poIFactory = new CFactoryfromAccess();”即可实现数据库的更换。
此时,问题来了。如果要在数据库增加一张表,用来存放公司部门信息,并对部门信息对象进行操作,部门对象也用员工对象定义类似的方法定义如下。
typedef struct Department { int nID; TString tstrDepartmentName; TString tstrManager; }; class IDepartment { public: virtual bool InserttoDB(Department& stDepartment) = 0; virtual Department GetDepartment(int nID) = 0; }; class CDepartmentfromMysql : public IDepartment { public: bool InserttoDB(Department& stDepartment) { _tprintf(_T("Insert Department %s into mysql\n"), stDepartment.tstrDepartmentName.c_str()); return true; } Department GetDepartment(int nID) { Department stDepartment; printf("Get an Department from mysql by id %d\n", nID); return stDepartment; } }; class CDepartmentfromAccess : public IDepartment { public: bool InserttoDB(Department& stDepartment) { _tprintf(_T("Insert Department %s into access\n"), stDepartment.tstrDepartmentName.c_str()); return true; } Department GetDepartment(int nID) { Department stDepartment; printf("Get an Department from access by id %d\n", nID); return stDepartment; } };
此时,如果仍使用工厂方法模式来处理,则可能会增加如下一行代码,来生产部门对象。
IDepartmentFactory* poIDepartmentFactoryFactory = new CDepartmentFactoryfromMysql();
如果采用的是简单工厂模式,由于要根据需要返回 IEmployee 和 IDepartment 2种对象,一个简单工厂已完全无法满足需求了,二个简单工厂(一个生产 IEmployee 对象,另一个生产 IDepartment 对象)则会发现一种数据库的操作都需要2个工厂类对象来处理,就像是想买苹果的手机需要找苹果公司去买,但买苹果的平板,却告知要到富士康公司去买了,已经失去简单工厂的本质意义了,也就是说简单工厂在此种情况下已经完全不能适应时代发展的需要了。工厂方法模式也有类似问题,如果需要更换数据库,需改更改2个工厂对象的用户代码,如果数据库中还要增加更多类型的信息操作,那么还需要增加更多的工厂对象,改动将越来越庞大。此时,该抽象工厂模式来拯救世界了,而且,实际上工厂方法模式早已为这一天准备了很久很久.....
2、抽象工厂模式拯救世界
2.1 抽象工厂模式的实现
class IFactory { public: ~IFactory() {} virtual IEmployee* CreateEmployee() = 0; virtual IDepartment* CreateDepartment() = 0; }; class CFactoryfromMysql : public IFactory { public: IEmployee* CreateEmployee() { return new CEmployeefromMysql(); } IDepartment* CreateDepartment() { return new CDepartmentfromMysql(); } }; class CFactoryfromAccess : public IFactory { public: IEmployee* CreateEmployee() { return new CEmployeefromAccess(); } IDepartment* CreateDepartment() { return new CDepartmentfromAccess(); } };
抽象工厂模式拯救世界的方式很简单,就是在抽象工厂早已准备好的工厂类接口 IFactory 中再小小的增加一个返回 IDepartment 部门对象的接口,然后所有的数据库工厂类中再实现生产 IDepartment 部门对象接口。
2.2 抽象工厂模式使用示例
void Test() { IFactory* poIFactory = new CFactoryfromMysql(); IEmployee* poIEmployee = NULL; if (!poIFactory) { return; } poIEmployee = poIFactory->CreateEmployee(); if (poIEmployee) { Employee stEmployee; stEmployee.nID = 1; stEmployee.tstrName = _T("Jim"); poIEmployee->InserttoDB(stEmployee); delete poIEmployee; } IDepartment* poIDepartment = poIFactory->CreateDepartment(); if (poIDepartment) { Department stDepartment; stDepartment.nID = 2; stDepartment.tstrDepartmentName = _T("Marketing"); stDepartment.tstrManager = _T("Jim"); poIDepartment->InserttoDB(stDepartment); delete poIDepartment; } delete poIFactory; }
基本员工对象的部分不用变化,再增加部门对象的的代码即可,如果需要更换数据库,则只需要改动“IFactory*poIFactory = new CFactoryfromMysql();”一处即可。
抽象工厂模式解决了不论用户要买 iphone 还是要买 ipad,都只需要找苹果公司就可以了,如果要买小米或者小米平板,则统统找小米公司即可,工厂方法模式是一个工厂只生产一种产品,但当公司壮大升级为集团公司时,则一个公司可能会生产N种产品,那么此时需要抽象工厂模式来拯救公司的未来了。
世界安静片刻后,问题依然存在,尽管抽象工厂模式很牛X,但依然没有解决简单工厂模式和工厂方法模式遗留下来的问题——违背开放封闭原则,《大话设计模式》一书中最终给出了解决方案——采用反射,但反射这么高大上的东东在C++中是不存在的。C++是否真的就无法给出一个圆满的答案?是否最终拯救世界的重任还需C#来完成?欲知后事如何,请听下回分解。。。。。。