例子是大话设计模式上第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图: