在簡單的三層登陸完成之後,我又在其中加入了設計模式,其中包括抽象工廠+反射和外觀模式.關於設計模式,在學習三層之前我們已經系統的學習過,可是在這次往機房收費系統中加設計模式時,還是感覺無從下手,出現了學沒有致用的尴尬情景.不過這也體現了我們提高班學習中項目驅動的優勢.
接下來是我的一些現有思路,可能還不很准確,期待讀者朋友的指點.
先說一下簡單的三層登陸思想吧:
我們現階段接觸的三層就是最基本的三層--UI層,BLL層和DAL層.關於這三者之間的關系,在我前面博客中有所涉及,如果有興趣,你可以參看:三層架構入門.
言歸正傳,理論知識我們知道了,在機房收費系統中,我們究竟是如何利用這三層來簡化靈活我們的代碼的呢?(由於.NET版本的登陸已經加入設計模式,所以簡單三層用C#語言來展示.)
大家可以先看下主體代碼:
UI層: 代碼如下:
[csharp]
<SPAN style="FONT-SIZE: 18px">private void btnOK_Click(object sender, EventArgs e)
{
UserInfo user = new UserInfo();
//向實體層傳遞參數
string UserName = txtUserName.Text.Trim();
string Password = txtPassword.Text.Trim();
user.UserName=UserName ;
user.Password = Password;
//實例化BLL層,並調用BLL層方法
LoginManager mgr = new LoginManager();
UserInfo user2 = new UserInfo();
user2 = mgr.UserLogin(user);
MessageBox.Show("登錄用戶:" + user.UserName);
}
</SPAN>
private void btnOK_Click(object sender, EventArgs e)
{
UserInfo user = new UserInfo();
//向實體層傳遞參數
string UserName = txtUserName.Text.Trim();
string Password = txtPassword.Text.Trim();
user.UserName=UserName ;
user.Password = Password;
//實例化BLL層,並調用BLL層方法
LoginManager mgr = new LoginManager();
UserInfo user2 = new UserInfo();
user2 = mgr.UserLogin(user);
MessageBox.Show("登錄用戶:" + user.UserName);
}
U層,就是我們窗體類,是用戶唯一可以看到的部分.其中不涉及連接數據庫,也不會包括業務邏輯.它主要負責接收用戶輸入指令並傳給實體類,再有實體類傳給B層,進行業務邏輯的判斷.同時它也會根據B層產生的不同結果來及時反饋給用戶.
BLL層: 代碼如下:
[csharp]
<SPAN style="FONT-SIZE: 18px">public class LoginManager
{
public UserInfo UserLogin(UserInfo user)
{
//實例化DAL層,並調用DAL層方法
UserDAO uDAO = new UserDAO();
user = uDAO.SelectUser(user );
//邏輯判斷代碼
if (user != null)
{
ScoreDAO sDAO = new ScoreDAO();
sDAO.UpdateScore(user, 10);
return user;
}
else
{
throw new Exception("登錄失敗!");
}
}
</SPAN>
public class LoginManager
{
public UserInfo UserLogin(UserInfo user)
{
//實例化DAL層,並調用DAL層方法
UserDAO uDAO = new UserDAO();
user = uDAO.SelectUser(user );
//邏輯判斷代碼
if (user != null)
{
ScoreDAO sDAO = new ScoreDAO();
sDAO.UpdateScore(user, 10);
return user;
}
else
{
throw new Exception("登錄失敗!");
}
}
這一層是業務邏輯層,顧名思義,它主要負責系統中的絕大部分邏輯判斷,一旦U層或者D層出現業務邏輯判斷,我們就要考慮,它是不是應該放在B層.
DAl層: 代碼如下:
[csharp]
<SPAN style="FONT-SIZE: 18px">namespace Login.DAL
{
public class UserDAO
{
/// <summary>
/// 連接數據庫,獲取用戶信息
/// </summary>
/// <param name="user">用戶信息實體</param>
/// <returns>用戶信息</returns>
public UserInfo SelectUser(UserInfo user)
{
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString)) //有待填寫
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"SELECT ID, UserName,Password,Email from Users where UserName=@UserName and Password=@Password";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter ("@UserName",user.UserName ));
cmd.Parameters.Add(new SqlParameter ("@Password",user.Password ));
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
user=null;
while (reader .Read ())//Read()方法:讀取一個表的記錄
{
if (user == null)
{
user = new UserInfo();
}
user.ID = reader.GetInt32(0);
user.UserName = reader.GetString(1);
user.Password = reader.GetString(2);
if (!reader.IsDBNull(3))
{
user.Email = reader.GetString(3);
}
}
return user;
}
}
}
}
</SPAN>
namespace Login.DAL
{
public class UserDAO
{
/// <summary>
/// 連接數據庫,獲取用戶信息
/// </summary>
/// <param name="user">用戶信息實體</param>
/// <returns>用戶信息</returns>
public UserInfo SelectUser(UserInfo user)
{
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString)) //有待填寫
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"SELECT ID, UserName,Password,Email from Users where UserName=@UserName and Password=@Password";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter ("@UserName",user.UserName ));
cmd.Parameters.Add(new SqlParameter ("@Password",user.Password ));
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
user=null;
while (reader .Read ())//Read()方法:讀取一個表的記錄
{
if (user == null)
{
user = new UserInfo();
}
user.ID = reader.GetInt32(0);
user.UserName = reader.GetString(1);
user.Password = reader.GetString(2);
if (!reader.IsDBNull(3))
{
user.Email = reader.GetString(3);
}
}
return user;
}
}
}
}
從以上的代碼就可以看出,D層主要是連接數據庫,從數據庫中讀取信息,然後再以實體的形式傳給B層,B層傳給U層,最後表現在用戶面前.
上述是沒有添加設計模式的簡單三層登陸,下面就要談一談為什麼要加設計模式,以及如何添加設計模式了.
設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結.使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。也許這樣說讀者還是不大明白,下面的代碼展示或許可以給您一些啟示.
首先說一下抽象工廠+反射,在機房登錄中的應用(只展示主體代碼,方便理解).
Abstract Factory: 抽象工廠接口,它裡面包含所有產品創建的抽象方法.工廠類被BLL層調用,並調用IDAL接口,使用配置文件讀取數據庫字符串.通過反射技術,解除簡單工廠更換數據庫時的分支判斷帶來的耦合.
代碼如下:
[vb]
<SPAN style="FONT-SIZE: 18px">Imports System.Configuration
Imports System.Reflection
Imports IDAL
Public Class SqlFactory
Private Shared ReadOnly AssemblyName As String = "DAL"
'使用配置文件,讀取數據庫連接字符
Private Shared DB As String = ConfigurationManager.AppSettings("strDB")
#Region "獲取用戶信息"
Public Shared Function CreatGetUserInfo() As IDAL.IGetUserInfo
Dim UserInfo As IGetUserInfo
Dim ClassName As String
'反射技術,解除簡單工廠更換數據庫時的分支判斷帶來的耦合
ClassName = AssemblyName + "." + DB + "D_GetUserInfo"
UserInfo = CType(Assembly.Load(AssemblyName).CreateInstance(ClassName), IDAL.IGetUserInfo)
Return UserInfo
End Function
#End Region
</SPAN>
Imports System.Configuration
Imports System.Reflection
Imports IDAL
Public Class SqlFactory
Private Shared ReadOnly AssemblyName As String = "DAL"
'使用配置文件,讀取數據庫連接字符
Private Shared DB As String = ConfigurationManager.AppSettings("strDB")
#Region "獲取用戶信息"
Public Shared Function CreatGetUserInfo() As IDAL.IGetUserInfo
Dim UserInfo As IGetUserInfo
Dim ClassName As String
'反射技術,解除簡單工廠更換數據庫時的分支判斷帶來的耦合
ClassName = AssemblyName + "." + DB + "D_GetUserInfo"
UserInfo = CType(Assembly.Load(AssemblyName).CreateInstance(ClassName), IDAL.IGetUserInfo)
Return UserInfo
End Function
#End Region
IDAL接口: 被B層和D層調用,使得B層和D層的耦合度極大降低.
[vb]
<SPAN style="FONT-SIZE: 18px">Imports Entity
Public Interface IGetUserInfo
Function GetUserInfo(ByVal e_Users As Entity.E_UserInfoEntity) As DataTable
End Interface
</SPAN>
Imports Entity
Public Interface IGetUserInfo
Function GetUserInfo(ByVal e_Users As Entity.E_UserInfoEntity) As DataTable
End Interface
BLL層: 定義接口類,並使用工廠類SqlFactory方法反射實例化相應的DAl類.這時候B層和D層已經完全不再耦合,B層或者D層的任何變動,都不會影響另一層.(這就是我們使用抽象工廠加反射的最大用處了.)
[vb]
<SPAN style="FONT-SIZE: 18px">Imports Entity
Imports IDAL
Imports DBFactory.SqlFactory
Imports DAL
Public Class B_UserInfo
'創建工廠類,作用:創建接口對象
Public SqlFactory As New DBFactory.SqlFactory
#Region "驗證用戶密碼"
Public Function VerifyPassword(ByVal EUserInfo As E_UserInfoEntity) As Boolean
'創建獲得用戶信息的接口
Dim IGetPWD As IDAL.IGetUserInfo
IGetPWD = DBFactory.SqlFactory.CreatGetUserInfo
Dim dtUserInfo As DataTable
dtUserInfo = IGetPWD.GetUserInfo(EUserInfo)
If dtUserInfo.Rows.Count = 0 Then
Throw New Exception("該用戶還未注冊!")
Return False
End If
'判斷密碼是否正確
If EUserInfo.Passwrod = Trim(dtUserInfo.Rows(0).Item(1)) Then
Return True
Else
Return False
End If
End Function
#End Region
End Class
</SPAN>
Imports Entity
Imports IDAL
Imports DBFactory.SqlFactory
Imports DAL
Public Class B_UserInfo
'創建工廠類,作用:創建接口對象
Public SqlFactory As New DBFactory.SqlFactory
#Region "驗證用戶密碼"
Public Function VerifyPassword(ByVal EUserInfo As E_UserInfoEntity) As Boolean
'創建獲得用戶信息的接口
Dim IGetPWD As IDAL.IGetUserInfo
IGetPWD = DBFactory.SqlFactory.CreatGetUserInfo
Dim dtUserInfo As DataTable
dtUserInfo = IGetPWD.GetUserInfo(EUserInfo)
If dtUserInfo.Rows.Count = 0 Then
Throw New Exception("該用戶還未注冊!")
Return False
End If
'判斷密碼是否正確
If EUserInfo.Passwrod = Trim(dtUserInfo.Rows(0).Item(1)) Then
Return True
Else
Return False
End If
End Function
#End Region
End Class
抽象工廠和反射解耦和B層和D層.接下來的外觀模式就要解耦U層和B層了.
Facade類: 在簡單的三層中,界面層的登陸需要根據B層返回的值再進行判斷,這樣B層和U層的耦合度是比較高的.而加入外觀模式後,上面提到的對B層返回值進行判斷就可以移到外觀類中.這樣就充分實現了B層和U層的分離.
[vb]
<SPAN style="FONT-SIZE: 18px">Imports Entity
Imports BLL
Public Class Login
Private bUserInfo As New B_UserInfo
Dim blnFlag As Boolean = False
''' <summary>
''' 對B層的返回值進行邏輯判斷
''' </summary>
''' <param name="eUserInfo">用戶信息實體</param>
''' <returns>用戶信息正確返回True,錯誤返回False</returns>
''' <remarks></remarks>
Public Function IsPassword(ByVal eUserInfo As E_UserInfoEntity) As Boolean
Try
blnFlag = bUserInfo.VerifyPassword(eUserInfo)
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示信息")
End Try
If blnFlag = True Then
Return True
Else
MsgBox("密碼錯誤,請重新輸入!")
Return False
End If
End Function
End Class
</SPAN>
Imports Entity
Imports BLL
Public Class Login
Private bUserInfo As New B_UserInfo
Dim blnFlag As Boolean = False
''' <summary>
''' 對B層的返回值進行邏輯判斷
''' </summary>
''' <param name="eUserInfo">用戶信息實體</param>
''' <returns>用戶信息正確返回True,錯誤返回False</returns>
''' <remarks></remarks>
Public Function IsPassword(ByVal eUserInfo As E_UserInfoEntity) As Boolean
Try
blnFlag = bUserInfo.VerifyPassword(eUserInfo)
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示信息")
End Try
If blnFlag = True Then
Return True
Else
MsgBox("密碼錯誤,請重新輸入!")
Return False
End If
End Function
End Class
UI層: 外觀層在對B層返回值進行判斷之後,返回信息給UI層.這樣U層和B層就實現了分離,兩者中任何一層發生變化都不用修改另一層的代碼.
[vb]
<SPAN style="FONT-SIZE: 18px">Imports Entity
Imports BLL
Imports Facade.Login
Public Class frmLogin
Public bUserInfo As New B_UserInfo
Public faUserInfo As New Facade.Login
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
Dim aUserInfo As New Entity.E_UserInfoEntity
Dim blnFlag As Boolean = False
aUserInfo.UserID = Trim(txtUserName.Text)
aUserInfo.Passwrod = Trim(txtPassword.Text)
'調用外觀類的IsPassword方法進行邏輯判斷
Try
blnFlag = faUserInfo.IsPassword(aUserInfo)
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示信息")
End Try
If blnFlag = True Then
frmMain.Show()
End If
End Sub
End Class
</SPAN>
Imports Entity
Imports BLL
Imports Facade.Login
Public Class frmLogin
Public bUserInfo As New B_UserInfo
Public faUserInfo As New Facade.Login
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
Dim aUserInfo As New Entity.E_UserInfoEntity
Dim blnFlag As Boolean = False
aUserInfo.UserID = Trim(txtUserName.Text)
aUserInfo.Passwrod = Trim(txtPassword.Text)
'調用外觀類的IsPassword方法進行邏輯判斷
Try
blnFlag = faUserInfo.IsPassword(aUserInfo)
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示信息")
End Try
If blnFlag = True Then
frmMain.Show()
End If
End Sub
End Class
上述是我現階段對三層和部分設計模式的理解,錯誤之處希望讀者朋友多多指教!