实际应用中,若更换数据库,维护两份代码,甚至更多。不方便
经典错误做法
class User{
private:
int id;
string name;
public:
void setId();
void getId();
void setName();
void getName();
}
class SqlServerUser{
public:
void insertUser(User user);
void getUser(int id);
}
int main()
{
SqlServerUser *sql = new SqlServerUser ();
User user;
sql.insertUser(user);
sql.getUser(1);
}
SqlServerUser *sql = new SqlServerUser ();此处使得sql这个对象被SqlServer限定死了,更换为Access则不行。如果是多态的则就可以。
工厂方法模式实现
工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪一个类
工厂方法模式代码
// user 表结构定义
class User {
private:
int id;
string name;
public:
User() { id = 1; name = "ts"; }
void setId() {}
void getId() {}
void setName() {}
void getName() {}
};
// 部门 表结构定义
class Department {};
//接口 部门表结构
class IDepartment {
public:
virtual void Insert(Department* de)
{
cout << "IDepartment :insert" << endl;
}
virtual void GetDepartment()
{
cout << "GetDepartment" << endl;
}
};
//SqlServerDepartment类 用于访问Sql Server 的Department
class SqlServerDepartment :public IDepartment {
public:
void Insert(Department* de)
{
cout << "SqlServerDepartment :insert" << endl;
}
void GetDepartment()
{
cout << "SqlServerDepartment :GetDepartment" << endl;
}
};
//AccessDepartment类 用于访问AccessDepartment 的Department
class AccessDepartment :public IDepartment {
public:
void Insert(Department* de)
{
cout << "AccessDepartment :insert" << endl;
}
void GetDepartment()
{
cout << "AccessDepartment :GetDepartment" << endl;
}
};
//接口USer类,用于客户端访问,解除与具体数据库访问的耦合
class IUser {
public:
IUser() { cout << "this is IUser" << endl; }
virtual void InsertUser(User *user )
{
cout << "IUser Insert " << endl;
}
virtual void GetUser(int id)
{
cout << "IUser getUser " << endl;
}
};
//SqlServerUser 类,用于访问Sql Server 的User表
class SqlServerUser :public IUser {
public:
SqlServerUser() { cout << "this is SqlServerUser" << endl; }
void InsertUser(User *user)
{
cout << "SqlServerUser insert" << endl;
}
void GetUser(int id)
{
cout << "SqlServerUser getId" << endl;
}
};
//AccessUser 类,用于访问Access 的User表
class AccessUser :public IUser {
public:
void insertUser(User *user)
{
cout << "AccessUser insert" << endl;
}
void GetUser(int id)
{
cout << "AccessUser getId" << endl;
}
};
//IFactoty接口,定义一个创建访问User表对象的抽象工厂接口
class IFactory {
public:
virtual IUser* CreateUser()
{
cout << "IFactory CreateUser() " << endl;
IUser* iu = NULL; //此处纯为了返回一个数,让编译通过
return iu;
}
//每新增表都需要增加一个创建该表的函数
virtual IDepartment* CreateDepartment()
{
cout << "IFactory CreateDepartment() " << endl;
IDepartment* iu = NULL; //此处纯为了返回一个数,让编译通过
return iu;
}
};
//SqlServerFactory类,实现IFactory接口,实例化SqlServerUSer
class SqlServerFactory:public IFactory {
public:
SqlServerFactory() { cout << "this is SqlServerFactory() " << endl; }
IUser* CreateUser()
{
cout << "SqlServerFactory CreateUser() " << endl;
return new SqlServerUser();
}
IDepartment* CreateDepartment()
{
cout << "IFactory CreateDepartment() " << endl;
return new SqlServerDepartment();
}
};
//AccessFactory类,实现IFactory接口,实例化AccessUSer
class AccessFactory :public IFactory {
public:
virtual IUser* CreateUser()
{
cout << "AccessFactory CreateUser() " << endl;
return new AccessUser();
}
IDepartment* CreateDepartment()
{
cout << "IFactory CreateDepartment() " << endl;
return new AccessDepartment();
}
};
//新增部门表 则新增三个类
int main()
{
std::cout << "Hello World!\n";
User *user = new User();
IFactory* factory = new SqlServerFactory(); //若要更改数据库将 new SqlServerFactory()改为new AccessFactory()
IUser *iu = factory->CreateUser(); //用户表接口类 负责操作用户表
iu->GetUser(1);
iu->InsertUser(user);
cout << "++++++++++++" << endl;
Department* de = new Department();
IDepartment* idepartment = factory->CreateDepartment();
idepartment->Insert(de);
idepartment->GetDepartment();
}
工厂方法模式UML图
这样只需要修改IFactory factory = new AccessFactory() 为IFactory factory = new SqlServerFactory()就实现数据库的访问了。当只有一个User类和User操作类时,是只需要工厂方法模式的,但数据库中有很多的表,而SqlServer 与Access又是两大不同的分类,解决这种涉及到多个产品系列的问题,有一个专门的工厂模式叫抽象工厂模式。
抽象工厂模式(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
UML结构图
AbstractProducetA和AbstractProducetB是两个抽象产品,之所以为抽象,是因为它们都有两种不同的实现。(User和Department),ProducetA1,ProducetA2和ProducetB1,ProducetB2就是对两个抽象产品具体分类的不同实现.ProducetA1可以理解为SqlServerUser,ProducetB1就是AccessUser。
IFactory是一个抽象工厂接口,它里面应该包含了所有产品创建的抽象方法。ConcreteFactory1和ConcreteFactory2就是具体的工厂了,就像SqlServerFactory 和AccessFactory一样。
通常是在运行时刻在创建一个ConcreteFactory类的实例,这个具体的工厂在创建具有特定实现的产品对象,也就是说,为创建不同的对象,客户端应使用不同的具体工厂。
1.抽象工厂模式的优缺点:最大的好处便是易于交换产品系列,由于具体工厂类。如IFactory fa = new AccessIFactory (),在一个应用中只需要初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。我们的设计不能防止需求的更改,那么我们的理想是让改动的最小,现在如果你要更改数据库的访问,只需要更改具体的工厂就可以做到。
第二好处:它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被与具体工厂的实现分离,不会出现在客户代码中。刚才的例子,客户端认识的只有IUser和IDepartment,至于它是用SqlServer还是Access实现就不知道了。
缺点:1.可以很方便的切换两个数据库访问的代码,但若增加一张项目表,则至少增加三个类IProject,SqlServerProject, AccessProject 还要改IFactory,SqlserverFactory 和AccessFactory 才可以完全实现。
2 . 客户端程序显然不是只有一个,很多地方都在使用IUser或Department,这样的设计在每个类的开始都要声明IFactory fa = new AccessIFactory ()。若有100个调用数据库访问的类,则改100次。
用简单工厂改进抽象工厂
去除IFactory SqlServerFactory AccessFactory三个工厂类,用一个简单的工厂模式实现。
C++代码实现
#include<iostream>
using namespace std;
// user 表结构定义
class User {
private:
int id;
string name;
public:
User() { id = 1; name = "ts"; }
void setId() {}
void getId() {}
void setName() {}
void getName() {}
};
// 部门 表结构定义
class Department {};
//接口 部门表结构
class IDepartment {
public:
virtual void Insert(Department* de)
{
cout << "IDepartment :insert" << endl;
}
virtual void GetDepartment()
{
cout << "GetDepartment" << endl;
}
};
//SqlServerDepartment类 用于访问Sql Server 的Department
class SqlServerDepartment :public IDepartment {
public:
void Insert(Department* de)
{
cout << "SqlServerDepartment :insert" << endl;
}
void GetDepartment()
{
cout << "SqlServerDepartment :GetDepartment" << endl;
}
};
//AccessDepartment类 用于访问AccessDepartment 的Department
class AccessDepartment :public IDepartment {
public:
void Insert(Department* de)
{
cout << "AccessDepartment :insert" << endl;
}
void GetDepartment()
{
cout << "AccessDepartment :GetDepartment" << endl;
}
};
//接口USer类,用于客户端访问,解除与具体数据库访问的耦合
class IUser {
public:
IUser() { cout << "this is IUser" << endl; }
virtual void InsertUser(User* user)
{
cout << "IUser Insert " << endl;
}
virtual void GetUser(int id)
{
cout << "IUser getUser " << endl;
}
};
//SqlServerUser 类,用于访问Sql Server 的User表
class SqlServerUser :public IUser {
public:
SqlServerUser() { cout << "this is SqlServerUser" << endl; }
void InsertUser(User* user)
{
cout << "SqlServerUser insert" << endl;
}
void GetUser(int id)
{
cout << "SqlServerUser getId" << endl;
}
};
//AccessUser 类,用于访问Access 的User表
class AccessUser :public IUser {
public:
void insertUser(User* user)
{
cout << "AccessUser insert" << endl;
}
void GetUser(int id)
{
cout << "AccessUser getId" << endl;
}
};
class DataAccess {
static const int dbType = 0; //0 sqlServer, 1 Access
public:
static IUser* CreateUser()
{
IUser* result = nullptr;
switch (dbType)
{
case 0:
result = new SqlServerUser();
break;
case 1:
result = new AccessUser();
break;
default:
break;
}
return result;
}
static IDepartment * CreateDepartment()
{
IDepartment * result = nullptr;
switch (dbType)
{
case 0:
result = new SqlServerDepartment();
break;
case 1:
result = new AccessDepartment();
break;
default:
break;
}
return result;
}
};
int main()
{
std::cout << "Hello World!\n";
User* user = new User();
IUser* iu = DataAccess::CreateUser();
iu->GetUser(1);
iu->InsertUser(user);
}
客户端改动不受数据库的影响,且客户端没有发现任何一个SqlServer 或Access的字样,达到了解耦的目的
不足之处,若新增Orcle数据库,则需要改动大量的代码,在DataAccess中增加switch代码的判断。
用反射+抽象工厂的数据访问程序
可以不在程序里写:如果是SqlServer就去实例化Sql Server数据库相关的类这样的语句而是根据字符串db的值去某个地方找应该实例化的类是哪一个。就不用switch了。
依赖注入。需要专门的Ioc容器提供比如Spring.Net。当前的不需要这么麻烦。只需要了解简单的.Net技术反射就可以了。
反射格式:Assembly.Load(“程序集名称”).CreateInstance(“命名空间.类名称”)
只需要在程序顶端写上using System.Reflection.来引用Reflection就可以使用反射来克服抽象工厂模式的先天不足了。
两种方法实例化效果是一样的,区别:反射实例化的类名是个字符串变量,可以根据需要修改。一个实例化是写死在程序里面的,而反射利用的是字符串变量。(写死太难听)将程序由编译时转为运行时。
可以用反射+配置文件实现数据库访问程序。可以在用简单工厂的地方,都可以考虑用反射技术去除switch或if,解除分支判断带来的耦合。