设计模式——(三)抽象工厂模式

实际应用中,若更换数据库,维护两份代码,甚至更多。不方便

经典错误做法

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,解除分支判断带来的耦合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值