實際使用時,按照功能頁面劃分,可以定義多個基類頁,如:
class FormBase
class BizFormBase :FormBase
class ViewFormBase : BizFormBase
class EditFormBase : BizFormBase
class QueryFormBase : FormBase
FormBase:基類頁中的根,提供與業務無關的服務,如:URL重寫,日誌等。
BizFormBase:加入和業務相關的屬性,如:該頁面的當前業務對象ID等。
ViewFormBase,:EditFormBase:實現具體的查看,編輯功能。
QueryFormBase:實現對通用查詢頁面的封裝。
從面向對象的角度看,基類頁與普通的基類,繼承類設計其實區別並不大,都要在基類中編寫公用的屬性方法,並通過虛函數、事件等方式讓繼承類重寫或響應。所不同的是基類頁的設計過程受到所在環境的約束。在WinForm環境下,我們可以預先定義好窗體的公用元素,如工具條,默認的表格以及DataSource控件等。而到了Asp.net下的WebForm,則無法實現界面一級的繼承,同時加入了狀態管理等要求。
爲了幫助大家理解,我們以查詢基類頁的設計過程來分析:
一個最簡單的查詢頁面會包括三部分,多個查詢條件文本框,查詢按鈕,以及表格。同時查詢頁會和一個數據訪問組件關聯,當點查詢時,會把查詢條件轉成where語句提交給數據訪問組件。
QueryPeopleForm
OnQueryButtonClick()
{
string peopleName = txtPeopleName.Text;
string peopleAge = txtPeopleAge.Text;
string sql;
sql = string.Format(“Name Like‘%{0}%’and Age = {1}, PeopleName, peopleAge);
PeopleManager manager = new PeopleManager();
this.gridMain.DataSource = manager.GetDataTable(sql);
this.gridMain.DataBind();
}
當點擊查詢按鈕時,我們做了以下三件事:
1、獲取查詢條件
2、提交查詢
3、將查詢結果綁定到表格
這兒的提交查詢和綁定在不同的查詢頁面都是一樣的,於是我們首先把2,3放到基類頁中,並提供這樣一個方法:void QueryAndBind(IManager manager, string sql); 這兒要定義IManager接口,讓所有的Manager都實現該接口,這樣基類頁就無需知道具體的Manager,只要調用IManager.GetDataTable方法,然後綁定表格到即可。
使用基類頁後的代碼:
QueryPeopleForm : QueryFormBase
OnQueryButtonClick()
{
string peopleName = txtPeopleName.Text;
string peopleAge = txtPeopleAge.Text;
sql = string.Format(“Name Like‘%{0}%’and Age = {1}, PeopleName, peopleAge);
QueryAndBind(new PeopleManager(), sql);
}
這兒的代碼少了,但仍有問題,當查詢條件變化後,每次拼查詢語句的工作即枯燥又容易出錯,那麼我們加入一個Query類,以簡化這兒的操作:
public enum QueryOperator
{
//等於比較。
Equal = 0,
// 不等於
NotEqual = 1,
// Like比較
Like = 6
}
class Query
{
void Add(string fieldName, string value, QueryOperator oper);
string GetSql();
}
QueryPeopleForm : QueryFormBase
OnQueryButtonClick()
{
Query query = new Query();
query.Add(“Name”, txtPeopleName.Text, QueryOperator.Like);
query.Add(“Age”, txtPeopleAge.Text, QueryOperator.Equal);
QueryAndBind(new PeopleManager(), query.GetSql());
}
把拼Sql的工作放在Query類中做,調用者只要聲明查詢字段,對應的值,和比較類型即可。
到這一步,我們的基類頁已經很好用了,但還有一個小問題,也就是前面說的,在WebForm中無法實現界面級的繼承,那麼基類頁的QueryAndBind方法,將無法知道查詢結果要綁定到哪一個表格,這時我們的做法是在基類頁中聲明DefaultGrid屬性,讓繼承頁來告知當前的表格控件。
修改後的代碼:
QueryPeopleForm : QueryFormBase
OnQueryButtonClick()
{
InitControls(gridMain);
Query query = new Query();
query.Add(“Name”, txtPeopleName.Text, QueryOperator.Like);
query.Add(“Age”, txtPeopleAge.Text, QueryOperator.Equal);
QueryAndBind(new PeopleManager(), query.GetSql());
}
至此,基類頁的功能已經完整了,但仍然不夠,如果我們以後想改變查詢按鈕點擊的行爲,比如查詢結果爲空時,要彈出對話框提示,這時仍然要到處修改頁面代碼,這不是我們所希望的,於是我們將QueryButton的OnClick操作也放在基類頁了中執行,繼承頁只要初始化數據訪問組件和設置查詢條件即可。
QueryPeopleForm : QueryFormBase
void Initialize()
{
// 指定頁面對應的Manager
Manager = new PeopleManager();
// 綁定控件
InitControls(gridMain, btnQuery);
}
void GetQueryInfo(Query query)
{
//獲得查詢條件
query.Add(“Name”, txtPeopleName.Text, QueryOperator.Like);
query.Add(“Age”, txtPeopleAge.Text, QueryOperator.Equal);
}
QueryFormBase:
private IManager manager = null;
public IManager Manager
{
get { return manager; }
set { manager = value; }
}
void InitControls(GridView grid, Button queryButton)
{
this.defaultGrid = grid;
this.queryButton = queryButton;
queryButton.Click += new EventHandler(QueryButton_Click);
}
void QueryButton_Click(object sender, EventArgs e)
{
Query query = new Query();
GetQueryInfo(Query);
QueryAndBind(manager, query.GetSql());
}
總的代碼可以從附件中下載,大家可以加入斷點,看看基類,繼承類頁的代碼執行順序。
應該說基類的設計相對複雜,但好處是繼承頁的代碼變得清楚了,沒有多餘重複的代碼。
而基類頁的設計其實是有技巧的,總結起來有以下幾條:
1、首先以最直接的方式寫出頁面代碼
2、提取公用方法和添加輔助類
3、提取事件處理流程到基類頁中
4、在基類頁中設計需要繼承頁重載的方法與事件
從設計上來講,用基類頁的方式來統一操作、簡化頁面代碼是一種非常直觀的方式,缺點是隨着項目的演化,基類頁會變得大而全,不容易被新的項目重用,這時我們就可以考慮把其中的一部分功能放到用戶控件和自定義組件中來實現,以減少耦合性和提高重用性。