提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。工厂是创建产品的,抽象工厂就是创建抽象产品的。
当你想对接口的调用者屏蔽接口的实现细节,可以通过抽象工厂模式进行封装实现。典型的应用案例为对于一个支持多种RDMS自由切换的系统,可以通过分层架构配合抽象工厂进行实现。
数据访问层封装对数据库的增、删、改、查操作。
业务逻辑层封装各种业务操作。
表示层负责界面逻辑展示与接受用户输入。
业务实体负责在各层之间进行数据传递。
数据传递过程如下图所示:
假如业务需要根据不同业务数据规模的应用场景可以支持部署在SQLServer上也可以支持部署在Oracle上,那么以上简单的架构则难以支撑;再比如可以根据用户的喜好把系统部署为Web模式也可以部署为WinForm桌面应用模式,则需要更为复杂的架构设计,经过最小化改进,我们把架构做如下改进。
数据对象工厂——Abstract Factory的具体应用,负责根据配置文件或参数设置创建数据访问接口实例
数据访问接口,定义对各数据表的基本数据访问操作规范:Insert 、Update、Delete、Get
数据访问实现类(SQLServerDAL),定义实现数据访问接口的数据访问类,基于SQLServer实现基本数据操作。
数据访问实现类(OracleDAL),定义实现数据访问接口的数据访问类,基于Oracle实现基本数据操作
业务逻辑层不会直接调用数据访问实现类(SQLServerDAL或OracleDAL),而是通过数据对象工厂(抽象工厂)创建数据访问接口实例进行数据访问的实现。
工程结构
Web.config、App.config中增加一个表示访问SQLServer还是Oracle的配置项
<configuration>
<appSettings>
<add key="DBType" value="Accp.SQLServerDAL"/>
appSettings>
数据访问接口定义
using System;
using System.Collections.Generic;
using System.Text;
namespace Accp.IDAL
{
using Accp.Model;
public interface ITicketService
{
///
/// 查询符合条件的机票信息
///
/// 出发城市ID
/// 到达城市ID
/// 起飞时间
/// 返回符合条件的机票实体集合
List<Ticket> GetTicketByCondition(int fromCity, int toCity, string leaveDate);
}
}
数据访问实现类(SQLServer)
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace Accp.SQLServerDAL
{
using Accp.Model;
using Accp.IDAL;
public class ProvinceInfoService : IProvinceInfoService
{
///
/// 执行SQL查询,封装查询结果到实体类和集合
///
/// 要执行的SQL语句
/// SQL语句中的参数列表
/// 返回封装好的实体集合
private List<ProvinceInfo> GetBySql(string sql, SqlParameter[] values)
{
using (SqlDataReader reader = DBHelper.GetReader(DBHelper.CONSTR, sql, values))
{
List<ProvinceInfo> lst = new List<ProvinceInfo>();
ProvinceInfo province = null;
while (reader.Read())
{
province = new ProvinceInfo();
province.ProvinceId = Convert.ToInt32(reader["provinceId"]);
province.ProvinceName = reader["provinceName"] as string;
lst.Add(province);
}
reader.Close();
return lst;
}
}
#region IProvinceInfoService 成员
///
/// 根据省份ID,查询省份信息
///
/// 省份ID
/// 返回省份实体信息
public ProvinceInfo GetProvinceInfoById(int provinceId)
{
string sql = "select * from ProvinceInfo where provinceId=@provinceId";
SqlParameter[] values ={
new SqlParameter("@provinceId",provinceId)
};
List<ProvinceInfo> lst = this.GetBySql(sql, values);
if (lst != null && lst.Count > 0) return lst[0];
return null;
}
///
/// 查询所有省份
///
/// 返回所有省份实体集合
public List<ProvinceInfo> GetAllProvinceInfo()
{
string sql = "select * from ProvinceInfo";
return this.GetBySql(sql, null);
}
#endregion
}
}
数据访问实现类(Oracle)
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.OracleClient;
namespace Accp.OracleDAL
{
using Accp.Model;
using Accp.IDAL;
public class TicketService : ITicketService
{
private CityInfoService cityInfoService = new CityInfoService();
///
/// 执行SQL查询,封装查询结果到实体类和集合
///
/// 要执行的SQL语句
/// SQL语句中的参数列表
/// 返回封装好的实体集合
private List<Ticket> GetBySql(string sql, OracleParameter[] values)
{
using (OracleDataReader reader = DBHelper.GetReader(DBHelper.CONSTR, sql, values))
{
List<Ticket> lst = new List<Ticket>();
Ticket ticket = null;
while (reader.Read())
{
ticket = new Ticket();
ticket.TicketId = Convert.ToInt32(reader["ticketId"]);
ticket.FlightOrder = reader["flightOrder"] as string;
ticket.FromCity = this.cityInfoService.GetCityInfoById(Convert.ToInt32(reader["fromCity"]));
ticket.ToCity = this.cityInfoService.GetCityInfoById(Convert.ToInt32(reader["toCity"]));
ticket.Price = Convert.ToDecimal(reader["price"]);
ticket.LeaveDate = Convert.ToDateTime(reader["leaveDate"]);
ticket.RoomType = Convert.ToInt32(reader["roomType"]);
lst.Add(ticket);
}
reader.Close();
return lst;
}
}
#region ITicketService 成员
///
/// 查询符合条件的机票信息
///
/// 出发城市ID
/// 到达城市ID
/// 起飞时间
/// 返回符合条件的机票实体集合
public List<Ticket> GetTicketByCondition(int fromCity, int toCity, string leaveDate)
{
string sql = "select * from ticket where fromCity=:fromCity and toCity=:toCity and to_char(leaveDate,'yyyy-MM-dd')=:leaveDate";
OracleParameter[] values ={
new OracleParameter("fromCity",fromCity),
new OracleParameter("toCity",toCity),
new OracleParameter("leaveDate",leaveDate)
};
return GetBySql(sql, values);
}
#endregion
}
}
抽象工厂类实现
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Configuration;
namespace Accp.Factory
{
using Accp.IDAL;
public class DBFactory<T>
{
private static readonly string DBType = ConfigurationManager.AppSettings["DBType"];
///
/// 创建接口实例的方法
///
///
public static T CreateService()
{
Type type = typeof(T);
string fullType = DBType + "." + type.Name.Substring(1);
Assembly assembly = Assembly.Load(DBType);
T instance = (T)assembly.CreateInstance(fullType);
return instance;
}
}
}
业务逻辑层通过抽象工厂获取数据访问接口实例,进行数据访问,示例如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
namespace Accp.BLL
{
using Accp.IDAL;
using Accp.Factory;
using Accp.Model;
[DataObject]
public class TicketManager
{
private static ITicketService ticketService = DBFactory<ITicketService>.CreateService();
///
/// 查询符合条件的机票信息
///
/// 出发城市ID
/// 到达城市ID
/// 起飞时间
/// 返回符合条件的机票实体集合
[DataObjectMethod(DataObjectMethodType.Select)]
public static List<Ticket> GetTicketByCondition(int fromCity, int toCity, string leaveDate)
{
return ticketService.GetTicketByCondition(fromCity, toCity, leaveDate);
}
}
}
表示层代码略
本案例中
数据访问实现类(SQLServerDAL、OracleDAL)为实体产品;
数据访问接口(IDAL)为抽象产品;
数据对象工厂(DBFactory)为抽象工厂,屏蔽了在业务逻辑层对SQLServerDAL和OracleDAL的具体依赖,通过修改配置文件中DBType的值就可以实现对不同RDBMS的切换支持;
业务逻辑类(BLL)为客户;
完整代码案例下载地址