本篇我以理論和實踐(源代碼)兩個方面和大家探討一下我的方案,希望大家多提寶貴意見。
一、軟件架構的概念問題,什麼是軟件的架構?我的理解是:軟件的架構包括兩個方面的內容,一個是軟件的開發架構,一個是軟件的部署架構,所謂部署架構就是指部署時的分佈式,集羣等設計問題;開發架構就是我們平常說的軟件分層設計問題,也就是我們今天要談的問題。
二、何謂分層?分層的方式有幾種?分層也就是把一個大的軟件解決方案分成多個項目進行開發,分爲三種,一種是按照代碼的功能層次進行分層,分爲數據庫訪問層,業務邏輯層,UI層等,一種是按照要實現的功能模塊進行分層,例如新聞管理層,博客管理層等,第三種就是把前兩者結合起來進行分層:先按照代碼功能劃分好層次,然後再在每一層中分層成各個功能模塊。
三、面向接口能夠降低各層之間的依賴關係嗎?還需要什麼?面向接口只是把對對象的直接調用轉到了對接口的調用身上,但對接口的調用也需要對象的實例化爲前提,因爲有對象實例化的存在,所以接口並不能屏蔽掉背後的對象來完成功能;在這種情況下,依賴注入/控制反轉等概念應運而生,IOC容器出現了,它能夠讓對象自動實例化,並管理對象的生存空間。
四、說了這麼多,到底應該怎樣分?我們不妨從軟件的開發過程上來考慮,做軟件的都知道我們要進行需求分析,要有需求分析文檔,需求分析的任務就是把客戶的需求轉變爲對軟件功能的需求,主要內容包括:軟件要實現那些功能模塊,每個功能模塊下包含哪些業務操作,這些業務操作需要哪些頁面(或者窗口)支持,這些頁面大概都是什麼樣子的,上面都有什麼?有了這些信息,我們就可以開始我們的系統設計了。
我的方案中各個項目的引用關係如下:
說明:
Web:UI層,Asp.net Web應用程序項目,提供用戶界面。
UnityConfig:依賴注入配置層,類庫項目,對Service層和Business層進行實現配置以提供給Web調用Service層。
Service:爲Web層提供服務,類庫項目,裏面全是接口。
Business:商業邏輯層,通過調用DA數據訪問層和Entities層來實現Service層的服務接口。類庫項目。
Entities:實體層,類庫項目,存放業務實體,貧血型實體。
請注意:在上面的引用關係中,Web層並沒有直接引用Business層。
現在假設我們有這樣一個需求文檔:它只需要實現一個功能模塊,就是查找並顯示僱員列表。頁面形式如圖:
首先我們根據需求,先設計服務,因爲只有一個功能模塊,所以我們只需要一個接口類,再因爲只有一個操作功能(查找),所以我們只需要一個服務方法,那麼我們在Service層建立一個接口類:IQueryEmployeeService,並添加一個接口方法:QueryEmployee.
根據頁面所示,我們這個服務方法需要兩個對象參數,一個是查找的條件,一個是查找的結果;這樣我們就得到了兩個實體類。在Entities建立分別命名爲QueryEntry和ListEntry。
代碼如下:
namespace Xiaozhuang.Service
{
public interface IQueryEmployeeService
{
/// <summary>
/// 查詢僱員信息
/// </summary>
/// <param name="queryentity"></param>
/// <returns></returns>
List<ListEntry> QueryEmployee(QueryEntry queryentry);
}
}
namespace Xiaozhuang.Entities
{
public class QueryEntry
{
public string DepartmentID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeAge { get; set; }
public override string ToString()
{
return "DeaprtmentID:" + DepartmentID + "EmployeeName:" + EmployeeName + "EmployeeAge:" + EmployeeAge;
}
}
}
namespace Xiaozhuang.Entities
{
public class ListEntry
{
public string EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeSex { get; set; }
public int EmployeeAge { get; set; }
public string DepartmentName { get; set; }
public string MobilePhone { get; set; }
public override string ToString()
{
return "EmployeeID:" + EmployeeID + "EmployeeName:" + EmployeeName + "EmployeeSex:" + EmployeeSex;
}
}
}
請注意,業務實體的設計完全是參照頁面而來,對實體字段類型的設計也是參照頁面而來,多數都是String類型,因爲這個時候並不知道打算在數據庫中怎麼存儲這些數據。
當然在實際的項目中,因爲那個部門列表還需要建立部門的業務實體,這裏簡單其間略去,有了這些業務實體和服務接口,我們就可以設計數據庫了,數據庫設計完成後,系統設計的工作就完成了。
接下來就是在Business層具體的實現這個查找的方法了:
namespace Xiaozhuang.Business
{
public class QueryEmployeeBusiness :IQueryEmployeeService
{
#region IQueryEmployeeService 成員
public List<ListEntry> QueryEmployee(QueryEntry queryentry)
{
List<ListEntry> listEntry = new List<ListEntry>();
ListEntry entry1 = new ListEntry() { EmployeeID = "1", EmployeeName = "僱員1", EmployeeSex = "男", DepartmentName = "部門1", EmployeeAge = 30, MobilePhone = "123546789" };
ListEntry entry2 = new ListEntry() { EmployeeID = "2", EmployeeName = "僱員2", EmployeeSex = "女", DepartmentName = "部門2", EmployeeAge = 29, MobilePhone = "123546789" };
listEntry.Add(entry1);
listEntry.Add(entry2);
return listEntry;
}
#endregion
}
}
此處略去從數據庫查詢的方法,可以用Linq to sql 實現或者其他Orm工具。
接下來配置一下我們的UnityConfig層,就可以在Web層訪問這個查找僱員的服務了。
namespace Xiaozhuang.UnityConfig
{
public interface IContainerAccessor
{
IUnityContainer Container { get; }
}
public class UnityContainerConfig
{
public IUnityContainer GetIUnityContainer()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IQueryEmployeeService, QueryEmployeeBusiness>();
return container;
}
}
}
當然你也可以把這個配置寫到Web。Config中。
接下來的問題就是怎樣在Web層調用這個服務的問題了,這個問題其實還是比較複雜的,文章太長了大家都看煩了,我將在下次再詳細說說這個問題。