上篇文章介绍了简单工厂和工厂方法这次则主要介绍工厂三姐妹中的抽象工厂呢。
一、用了工厂方法的模式的数据库访问程序
<pre name="code" class="csharp"> class User
{
private int _id;
public int ID
{
get { return _id; }
set {_id = value ;}
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
//Iuser接口,用于客户端访问,解除与具体数据库访问的耦合
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
class SqlserverUser : IUser//Sqlserver类,用于访问SQL Server的User
{
public void Insert(User user)
{
Console.WriteLine("在SQL Server中给User表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine("在SQL Server中给User表增加一条记录");
return null;
}
}
<pre name="code" class="csharp"><span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">//Sqlserver类,用于访问Accessr的User</span>
class AccessUser : IUser
{ public void Insert(User user) { Console.WriteLine("在Access中给User表增加一条记录"); } public User GetUser(int id) { Console.WriteLine("在Access中根据给User表增加一条记录"); return null; } }
//定义一个创建访问User表对象的抽象工厂接口。
interface IFactory
{
IUser CreateUser();
}
//SQLServerFactory类,实现IFactory接口,实例化SQLserverUser
class SqlServerFactory : IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
}
<pre name="code" class="csharp">//AccessFactory类,实现IFactory接口,实例化AccessUser
class AccessFactory : IFactory { public IUser CreateUser() { return new AccessUser(); } }
其实上面的代码很简单,就是两个工厂类对应两种用户。
客户端代码:
User user = new User();
IFactory factory = new SqlServerFactory();
IUser iu = factory.CreateUser();
iu.Insert(user);
iu.GetUser(1);
Console.Read();
如果用简单工厂:去除IFactory、SQLserverFactory和AccessFactory三个工厂类,取而代之的是DataAccess类
class DataAcess
{
private static readonly string db = "Sqlserver";//数据库名称,可替换成Access
public static IUser CreateUser()
{
IUser result = null;
switch (db)
{
case "Sqlserver":
result = new SqlserverUser();
break;
case "Access":
result = new AccessUser();
break;
}
return result;
}
}
二、用了抽象工厂模式的数据库访问程序
对比:大家直观的从类图中就可以看出,抽象工厂方法只不过是比工厂方法多了部门,相当于数据库中除了用户表又增加了部门表,当只有一个User类和User操作类的时候,是只需要工厂方法模式的,但是当数据库中有很多表,而SQLServer与Access又是两大不同的分类,所以要解决这种涉及到多个产品系列的问题,就用到了抽象工厂模式。
代码只需要增加新的接口和类。
//IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合
interface IDepartment
{
void Insert(IDepartment department);
IDepartment GetDepartment(int id);
}
//SqlserverDepartment类,用于访问SQL Server的Department
class SqlserverDepartment:IDepartment
{
public void Insert(IDepartment dpartment)
{
Console .WriteLine ("在SQL Server中给Department表增加一条记录");
}
public IDepartment GetDepartment(int id)
{
Console .WriteLine ("在SQL Server中根据ID得到Department表一条记录");
return null ;
}
}
//AccessDepartment类,用于访问Access的Department
class SqlserverDepartment : IDepartment
{
public void Insert(IDepartment dpartment)
{
Console.WriteLine("在Access中给Department表增加一条记录");
}
public IDepartment GetDepartment(int id)
{
Console.WriteLine("在Access中根据ID得到Department表一条记录");
return null;
}
}
还要在相应的工厂类中增加部门Department工厂SqlServerFactory中相似的代码不再贴出。
public IDepartment CreateDeparment()
{
return new Accessdeparment();
}
抽象工厂的优点与缺点:
便于交换产品系列,只需要改变具体工厂即可使用不同的产品配置。并且让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
但是如果要再添加一个新表比如项目表就至少要增加三个类,IProject、SQLserverProject、AccessProject,三个类。编程是门艺术这样大批量的改动,显然是非常丑陋的做法。
解决这个问题的办法:
1.可以用上面提到的简单工厂方法进行改进直接在DateAccess中增加相应的表就可以了。
2.用反射
//DataAcess 类,用反射技术取代IFactory,SQLserverFactory和AccessFactory
//using System.Reflection ;//引入反射,必须写
class DataAccess
{
private static readonly string AssemblyName = "抽象工厂模式";
private static readonly string db ="Sqlserver";//数据库名称可以替换成Access
public static IUser CreateUser()
{
string className = AssemblyName +"."+ db +"User";
return (IUser )Assembly.Load (AssemblyName ).CreateInstance (className );
}
public static IDepartment CreateDepartment()
{
string className = AssemblyName +"."+ db +"Department";
return (IDepartment )Assembly.Load (AssemblyName ).CreateInstance (className );
}
}