【1】什么是抽象工厂模式?
原文:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
所有专业书上都是这句话:“为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。”
逐级分解一下:
1、为创建一组相关的对象提供一个接口,而且无需指定他们的具体类。
2、为创建一组相互依赖的对象提供一个接口,而且无需指定他们的具体类。
3、为创建对象提供一个接口,而且无需指定他们的具体类。
4、为创建对象提供一个接口。
5、一个接口。
抽象工厂:抽象是指接口,工厂是指集合,其实是接口集合。
既然是接口集合,那么属于哪种类型的接口集合?即具体工厂类。
比如SqlFactory,是指Sql数据库的接口集合。
而Sql数据库的接口集合最终都可以创建出哪种类型的Sql产品?即具体产品类。
比如SqlUser,SqlDepartment,是指具体的Sql产品类。
所以,抽象工厂,即创建出产品类的接口集合。
比如,IFactory,创建出产品类的接口集合,不可以实例化对象,仅仅提供接口规范。
每个具体工厂,即创建出同一类产品的功能集合。
比如,SqlFactory,创建出同一类产品SqlUser、SqlDepartment的功能集合。
【2】抽象工厂模式的逻辑结构图及代码示例:
逻辑结构图:
代码示例:
1 #include <string> 2 #include <iostream> 3 using namespace std; 4 5 // 抽象产品类1 6 class IUser 7 { 8 public: 9 virtual void getUser() = 0; 10 virtual void setUser() = 0; 11 }; 12 13 // 具体产品类1(SqlUser) 14 class SqlUser : public IUser 15 { 16 public: 17 void getUser() 18 { 19 cout << "在sql中返回user" << endl; 20 } 21 void setUser() 22 { 23 cout << "在sql中设置user" << endl; 24 } 25 }; 26 27 // 具体产品类1(AccessUser) 28 class AccessUser : public IUser 29 { 30 public: 31 void getUser() 32 { 33 cout << "在Access中返回user" << endl; 34 } 35 void setUser() 36 { 37 cout << "在Access中设置user" << endl; 38 } 39 }; 40 41 // 抽象产品类2 42 class IDepartment 43 { 44 public: 45 virtual void getDepartment() = 0; 46 virtual void setDepartment() = 0; 47 }; 48 49 // 具体产品类2(SqlDepartment) 50 class SqlDepartment : public IDepartment 51 { 52 public: 53 void getDepartment() 54 { 55 cout << "在sql中返回Department" << endl; 56 } 57 void setDepartment() 58 { 59 cout << "在sql中设置Department" << endl; 60 } 61 }; 62 63 // 具体产品类2(AccessDepartment) 64 class AccessDepartment : public IDepartment 65 { 66 public: 67 void getDepartment() 68 { 69 cout << "在Access中返回Department" << endl; 70 } 71 void setDepartment() 72 { 73 cout << "在Access中设置Department" << endl; 74 } 75 }; 76 77 // 抽象工厂类 78 class IFactory 79 { 80 public: 81 virtual IUser *createUser() = 0; 82 virtual IDepartment *createDepartment() = 0; 83 }; 84 85 // 具体工厂类(SqlFactory) 86 class SqlFactory : public IFactory 87 { 88 public: 89 IUser *createUser() 90 { 91 return new SqlUser(); // 创建具体产品1(SqlUser) 92 } 93 IDepartment *createDepartment() 94 { 95 return new SqlDepartment(); // 创建具体产品2(SqlDepartment) 96 } 97 }; 98 99 // 具体工厂类(AccessFactory) 100 class AccessFactory : public IFactory 101 { 102 public: 103 IUser *createUser() 104 { 105 return new AccessUser(); // 创建具体产品1(AccessUser) 106 } 107 IDepartment *createDepartment() 108 { 109 return new AccessDepartment(); // 创建具体产品2(AccessDepartment) 110 } 111 }; 112 113 void main() 114 { 115 IFactory *pFactory = NULL; 116 IUser *pUser = NULL; 117 IDepartment *pDepartment = NULL; 118 119 pFactory = new AccessFactory(); 120 if (NULL == pFactory) 121 { 122 return; 123 } 124 125 pUser = pFactory->createUser(); 126 if (NULL == pUser) 127 { 128 return; 129 } 130 pDepartment = pFactory->createDepartment(); 131 if (NULL == pDepartment) 132 { 133 return; 134 } 135 136 pUser->getUser(); 137 pUser->setUser(); 138 pDepartment->getDepartment(); 139 pDepartment->setDepartment(); 140 141 delete pFactory; 142 pFactory = NULL; 143 delete pUser; 144 pUser = NULL; 145 delete pDepartment; 146 pDepartment = NULL; 147 148 system("pause"); 149 } 150 151 // run out: 152 /* 153 在Access中返回user 154 在Access中设置user 155 在Access中返回Department 156 在Access中设置Department 157 请按任意键继续. . . 158 */
抽象产品类、抽象工厂类、具体产品类、具体工厂类如上注释。
【3】抽象工厂模式结构图
结构图如下所示:
为了加深宏观的理解,特搜了这张图贴上。
【4】工厂方法模式与抽象工厂模式的区别
详细对比请参见下图:
抽象工厂模式更难理解,需要用心琢磨,仔细体会。
【5】抽象工厂模式的优点和缺点
抽象工厂模式的优点:
1、便于交换产品系。
由于具体工厂类,例如IFactory factory = new SqlFactory(),在一个应用中只需要在初始化时出现一次。
这就使得改变一个应用的具体工厂变得很简单,而想要使用不同的产品配置只需要改变具体工厂即可。
2、客户端与具体创建实例过程分离。
客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
抽象工厂模式的缺点(程序也是很有原则性的,比如:开放-封闭原则):
针对第一点优点,试想第一点(封闭原则,对于修改尽量关闭):客户端程序显然不可能只有一处使用IUser或IDepartment。
而如此设计,如果有100处调用了数据库访问类呢?是不是就需要更改100次IFactory factory = new AccessFactory()这样的代码才行?
很明显,不能满足我们想改动一处就完全达到要求的目的。
第二点(开放原则,对于扩展尽量开放):仅仅能很方便的切换数据库访问代码是很有局限性的,假设用户的需求改变需要增加功能,比如增加项目表Project。
先明确一下需要改动的地方。至少增加三个类,IProject、SqlserverProject、AccessProject,还需要更改IFactory、SqlserverFactory、AccessFactory才可以完全满足要求。
以上俩个缺点,又让我们陷入深深的沉思。
【6】改进方案1:简单工厂改进抽象工厂
代码示例如下:
1 #include <string> 2 #include <iostream> 3 using namespace std; 4 5 // 抽象产品类1 6 class IUser 7 { 8 public: 9 virtual void getUser() = 0; 10 virtual void setUser() = 0; 11 }; 12 13 // 具体产品类1(SqlUser) 14 class SqlUser : public IUser 15 { 16 public: 17 void getUser() 18 { 19 cout << "在sql中返回user" << endl; 20 } 21 void setUser() 22 { 23 cout << "在sql中设置user" << endl; 24 } 25 }; 26 27 // 具体产品类1(AccessUser) 28 class AccessUser : public IUser 29 { 30 public: 31 void getUser() 32 { 33 cout << "在Access中返回user" << endl; 34 } 35 void setUser() 36 { 37 cout << "在Access中设置user" << endl; 38 } 39 }; 40 41 // 抽象产品类2 42 class IDepartment 43 { 44 public: 45 virtual void getDepartment() = 0; 46 virtual void setDepartment() = 0; 47 }; 48 49 // 具体产品类2(SqlDepartment) 50 class SqlDepartment : public IDepartment 51 { 52 public: 53 void getDepartment() 54 { 55 cout << "在sql中返回Department" << endl; 56 } 57 void setDepartment() 58 { 59 cout << "在sql中设置Department" << endl; 60 } 61 }; 62 63 // 具体产品类2(AccessDepartment) 64 class AccessDepartment : public IDepartment 65 { 66 public: 67 void getDepartment() 68 { 69 cout << "在Access中返回Department" << endl; 70 } 71 void setDepartment() 72 { 73 cout << "在Access中设置Department" << endl; 74 } 75 }; 76 77 // 工厂方法类 78 class DataAccess 79 { 80 private: 81 static string db; 82 83 public: 84 static IUser *createUser() 85 { 86 if (db == "sql") 87 { 88 return new SqlUser(); 89 } 90 else if (db == "access") 91 { 92 return new AccessUser(); 93 } 94 95 return NULL; 96 } 97 98 static IDepartment *createDepartment() 99 { 100 if (db == "sql") 101 { 102 return new SqlDepartment(); 103 } 104 else if (db == "access") 105 { 106 return new AccessDepartment(); 107 } 108 109 return NULL; 110 } 111 }; 112 113 string DataAccess::db = "sql"; 114 115 void main() 116 { 117 IUser *pUser = NULL; 118 IDepartment *pDepartment = NULL; 119 120 pUser = DataAccess::createUser(); 121 if (NULL == pUser) 122 return; 123 124 pDepartment = DataAccess::createDepartment(); 125 if (NULL == pDepartment) 126 return; 127 128 pUser->getUser(); 129 pUser->setUser(); 130 pDepartment->getDepartment(); 131 pDepartment->setDepartment(); 132 133 delete pUser; 134 pUser = NULL; 135 delete pDepartment; 136 pDepartment = NULL; 137 138 system("pause"); 139 } 140 141 // run out: 142 /* 143 在sql中返回user 144 在sql中设置user 145 在sql中返回Department 146 在sql中设置Department 147 请按任意键继续. . . 148 */
利用DataAccess(数据访问)类替换掉了IFactory、SqlserverFactory、AccessFactory三个工厂类。
【7】改进方案2:配置文件 + 抽象工厂模式
Good Good Study, Day Day Up.
顺序 选择 循环 总结