例子是大话设计模式上第15章的内容,用抽象工厂模式,实现了程序中业务逻辑与数据访问的解耦。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。侧重点:多个系列的话可以考虑。
适用性:
1 一个系统要独立于它的产品创建,组合与表示时
2 一个系统要由多个产品系列中的一个来配置时
3 当要情调一系列相关的产品对象的设计以便进行联合使用时。
4 当提供一个产品类库,而只想显示它们的接口而不是实现时
结构图:
与工厂方法的区别是,工厂方法只是一个抽象工厂类,并不对商品进行系列划分,这些上面是一样的关系,而抽象工厂,将商品分为不同系列的,而针对不同的系列对应有不同的工厂,一个系列对应一个工厂。
参与者:
AbstractorFactory:声明一个抽象产品对象的操作接口(定义了所有种类商品的创建接口)
ConcreteFactory:实现创建具体产品对象的操作(一个工厂对应这个系列的所有的商品的创建)
AbstractProduct: 为一类产品对象声明一个接口
ConcreteProduct:定义一个将被相应的具体工厂创建的产品对象,实现abstractProduct的接口
Client: 仅使用由AbstractFactory和AbstractProduct类声明的接口。
协作:
通常在运行时刻创建一个ConcreteFactory的实例,这一个具体的工厂创建具有特定实现的产品对象。为创建不同的产品对象,客户可以还具体工厂就可以实现了。
AbstractFactory将产品对象的创建延迟到它的ConcreteFactory的子类中
效果:
(1)分离了具体的类,工厂将客户与商品类的实现分离,客户不需要知道具体类的类名,只需要调用工厂中的接口
(2)易于交换产品系列,更改产品系列只需要更改一个具体工厂类的初始化。
(3)有利于产品的一致性:因为一系列的产品被设计到一个工厂中
(4)缺点阿,难以支持新种类的产品,因为抽象共存接口确定了可以被创建的产品集合,而添加一个新种类就要增加新的商品子类,然后队抽象工厂和具体工厂都需要更改。
实现:
(1)将工厂实现为单件模式:一个产品系列只需一个具体工厂的实例。
(2)创建产品:工厂方法模式是对每一个商品定义一个工厂方法。抽象工厂是要求队每个产品系列都要有一个新的工厂子类,即使差别很小。
(3)定义可扩展的工厂:改进它的缺点。
以下是书上问题的代码实现:
//抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口, //而无需指定他们的类。如SqlserverFactory提供创建Sqlserver相关的用户和部门 //优点:易于交换系列产品。具体的常见实例过程与客户端分离, //缺点:增加一个表后,需要更改三个工厂类 #include <iostream> #include <string> using namespace std; //User类,User表中的数据,供插入查询操作 class User { public: int GetId() { return id; } string GetName() { return name; } void SetId(int _id) { id = _id; } void SetName(string _name) { name = _name; } private: int id; string name; }; //User表的接口,有插入和查询操作 class IUser { public: virtual void Insert(User &user) { } virtual User* GetUser(int id) { return NULL; } }; //SqlserverUser表 class SqlserverUser : public IUser { public: void Insert(User &user) { cout << "在SQL Server中给User表增加一条记录" << endl; } User* GetUser(int id) { cout << "在SQL Server中根据ID得到User表一条记录" << endl; return NULL; } }; //AccessUser表 class AccessUser : public IUser { public: void Insert(User &user) { cout << "在Access中给User表增加一条记录" << endl; } User* GetUser(int id) { cout << "在Access中根据ID得到User表一条记录" << endl; return NULL; } }; //Department类,为Department插入数据 class Department { public: int GetId() { return id; } string GetName() { return deptname; } void SetId(int _id) { id = _id; } void SetName(string _deptname) { deptname = _deptname; } private: int id; string deptname; }; //IDpartment表的接口,又插入和查询操作 class IDepartment { public: virtual void Insert(Department &dept) { } virtual Department* GetDepartment(int id) { return NULL; } }; //SqlserverDepartment表 class SqlserverDepartment : public IDepartment { public: void Insert(Department &dept) { cout << "在SQL Server中给Department表增加一条记录" << endl; } Department* GetDepartment(int id) { cout << "在SQL Server中根据ID得到Department表一条记录" << endl; return NULL; } }; //AccessDepartment表 class AccessDepartment : public IDepartment { public: void Insert(Department &dept) { cout << "在Access中给Department表增加一条记录" << endl; } Department* GetDepartment(int id) { cout << "在Access中根据ID得到Department表一条记录" << endl; return NULL; } }; //工厂类,用于产生数据库中的各种表 class IFactory { public: virtual IUser* CreateUser() { return NULL; } virtual IDepartment* CreateDepartment() { return NULL; } }; //用于产生使用SqlServer数据库中的user表与department表 class SqlserverFactory : public IFactory { public: IUser* CreateUser() { return new SqlserverUser; } IDepartment* CreateDepartment() { return new SqlserverDepartment; } }; //用于产生使用Access数据库中的user表与department表 class AccessFactory : public IFactory { public: IUser* CreateUser() { return new AccessUser; } IDepartment* CreateDepartment() { return new AccessDepartment; } }; int main() { //供插入数据使用 User user; Department dept; //如果要更改数据库,只需要更改这一句new SqlserverFactory IFactory *factory = new AccessFactory(); //只知道创建了User,但是不知道创建的是什么User IUser *iuser = factory->CreateUser(); iuser->Insert(user); iuser->GetUser(1); IDepartment* idept = factory->CreateDepartment(); idept->Insert(dept); idept->GetDepartment(1); system("pause"); return 0; }
最大的好处是易于交换产品系列,由于具体工厂类,如IFactory factory = new AccessFactory(),在一个应用中只需要在初始化的时候出现一次,就可以改变一个应用的具体工厂变得非常容易,只需要改变具体工厂即可使用不同的产品配置。
第二个好处是:让具体的创建实例过程与客户端分离,客户端是通过它们你的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中,这就解决了工厂模式的需要修改客户端的问题。
但是模式都有缺点,这个针对这个问题,当需要增加一个项目表Project,则需要增加三个类,IProject,SqlserverProject, AccessProject,还需要更改IFactory,SqlserverFactory,AccessFactory才能实现。。。
GoF书上Maze例子的UML图: