Abstract Factory(抽象工廠)——物件建立型模式
Abstract Factory(抽象工廠)——物件建立型模式
意圖
提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。工廠是建立產品的,抽象工廠就是建立抽象產品的。
典型應用場景
當你想對介面的呼叫者遮蔽介面的實現細節,可以通過抽象工廠模式進行封裝實現。典型的應用案例為對於一個支援多種RDMS自由切換的系統,可以通過分層架構配合抽象工廠進行實現。
傳統三層架構
傳統三層架構包括資料訪問層、業務邏輯層、表示層,如下圖:
資料訪問層封裝對資料庫的增、刪、改、查操作。
業務邏輯層封裝各種業務操作。
表示層負責介面邏輯展示與接受使用者輸入。
業務實體負責在各層之間進行資料傳遞。
資料傳遞過程如下圖所示:
假如業務需要根據不同業務資料規模的應用場景可以支援部署在SQLServer上也可以支援部署在Oracle上,那麼以上簡單的架構則難以支撐;再比如可以根據使用者的喜好把系統部署為Web模式也可以部署為WinForm桌面應用模式,則需要更為複雜的架構設計,經過最小化改進,我們把架構做如下改進。
改進的三層架構
改進的三層架構如下圖:
通過擴充套件,資料訪問層由4部分構成
資料物件工廠——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.OracleDAL"/>
-->
<add key="DBType" value="Accp.SQLServerDAL"/>
</appSettings>
</configuration
資料訪問介面定義
using System;
using System.Collections.Generic;
using System.Text;
namespace Accp.IDAL
{
using Accp.Model;
public interface ITicketService
{
/// <summary>
/// 查詢符合條件的機票資訊
/// </summary>
/// <param name="fromCity">出發城市ID</param>
/// <param name="toCity">到達城市ID</param>
/// <param name="leaveDate">起飛時間</param>
/// <returns>返回符合條件的機票實體集合</returns>
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
{
/// <summary>
/// 執行SQL查詢,封裝查詢結果到實體類和集合
/// </summary>
/// <param name="sql">要執行的SQL語句</param>
/// <param name="values">SQL語句中的引數列表</param>
/// <returns>返回封裝好的實體集合</returns>
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 成員
/// <summary>
/// 根據省份ID,查詢省份資訊
/// </summary>
/// <param name="provinceId">省份ID</param>
/// <returns>返回省份實體資訊</returns>
public ProvinceInfo GetProvinceInfoById(int provinceId)
{
string sql = "select * from ProvinceInfo where [email protected]";
SqlParameter[] values ={
new SqlParameter("@provinceId",provinceId)
};
List<ProvinceInfo> lst = this.GetBySql(sql, values);
if (lst != null && lst.Count > 0) return lst[0];
return null;
}
/// <summary>
/// 查詢所有省份
/// </summary>
/// <returns>返回所有省份實體集合</returns>
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();
/// <summary>
/// 執行SQL查詢,封裝查詢結果到實體類和集合
/// </summary>
/// <param name="sql">要執行的SQL語句</param>
/// <param name="values">SQL語句中的引數列表</param>
/// <returns>返回封裝好的實體集合</returns>
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 成員
/// <summary>
/// 查詢符合條件的機票資訊
/// </summary>
/// <param name="fromCity">出發城市ID</param>
/// <param name="toCity">到達城市ID</param>
/// <param name="leaveDate">起飛時間</param>
/// <returns>返回符合條件的機票實體集合</returns>
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"];
/// <summary>
/// 建立介面例項的方法
/// </summary>
/// <returns></returns>
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();
/// <summary>
/// 查詢符合條件的機票資訊
/// </summary>
/// <param name="fromCity">出發城市ID</param>
/// <param name="toCity">到達城市ID</param>
/// <param name="leaveDate">起飛時間</param>
/// <returns>返回符合條件的機票實體集合</returns>
[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)為客戶;