起初,机房收费系统用纯三层在做,三层结构已把高内聚,低耦合的思想发挥的淋漓尽致。因为一直听说“抽象工厂”,也开始尝试去应用,经过了一番尝试,也OK了。其中当然会用到反射和配置文件,而接口的引入也是顺理成章的。接口对B层和D层又完成了进一步解耦。
下面就以登录(Login)这个小Demo中的部分功能为例来阐述过程的实现。
前面的一篇博客(机房收费系统总结之3——SqlHelper)介绍过,这里不再赘述。
三、IDAL'/************************************************************************************* '类 名 称:UserInfoEntity '命名空间:Entity '创建时间:2013-08-09 14:47:28 '作 者:张连海 '小 组: '修改时间: '修 改 人: '版 本 号:v1.0.0 '*************************************************************************************/ Public Class UserInfoEntity Private strUserName As String 'Hold the UserName property Private strPassword As String 'Hold the Password property Private strRealName As String 'Hold the RealName property Private strGmlevel As String 'Hold the Gmlevel property Private strAccountBy As String 'Hold the AccountBy property '设置UserName属性 Public Property UserName() As String Get Return strUserName End Get Set(value As String) strUserName = value End Set End Property '设置Password属性 Public Property Password() As String Get Return strPassword End Get Set(value As String) strPassword = value End Set End Property '设置RealName属性 Public Property RealName() As String Get Return strRealName End Get Set(value As String) strRealName = value End Set End Property '设置Gmlever属性 Public Property Gmlevel() As String Get Return strGmlevel End Get Set(value As String) strGmlevel = value End Set End Property '设置AccountBy属性 Public Property AccountBy() As String Get Return strAccountBy End Get Set(value As String) strAccountBy = value End Set End Property End Class
Imports Entity Public Interface IUserInfo '''
''' 调用SqlHelper把传来的用户基本信息添加到T_UserInfo表中 ''' ''' 用户信息实体类 '''返回执行 添加 语句受影响的行数,为Integer类型 Function AddUserInfo(ByVal enUserInfo As UserInfoEntity) As Integer Function DeleteUser(ByVal enUserInfo As UserInfoEntity) As Integer '''''' 调用SqlHelper完成对T_UserInfo表中相应用户的密码 ''' ''' 用户信息实体类 '''返回执行 更新 语句受影响的行数,为Integer类型 '''Function UpdatePassword(ByVal enUserInfo As UserInfoEntity) As Integer ''' ''' 调用SqlHelper来查询T_UserInfo表中相应用户信息 ''' ''' 用户信息实体类 '''返回执行 添加 语句受影响的行数,为Integer类型 Function SelectUserInfo(ByVal enUserInfo As UserInfoEntity) As DataTable End Interface
四、D层——SqlServerDAL
这一层的主要任务是直接操作数据库,完成对数据的增删改查等。这里我们仍然根据数据表来抽象DAL层的类,基本上也是一个表对应一个类,这样当我们增加新的表,直接增加新的DAL层类就可以,很好地符合了“开闭原则”。接口中的方法,也就是需要通过D层来实现的方法,在调用的过程中,D层就会将这个方法实现,返回相应的值 。但注意的是:DAL层类的数量也可能多余表的数量,可以定义一个接口叫ITime,这个接口是用来获取服务器时间。五、接下来就是重头戏了——抽象工厂(Factory)Imports System.Data.SqlClient Imports Entity Imports IDAL Public Class UserInfoDAL : Implements IDAL.IUserInfo Private clsSqlHelper As SqlHelper.SqlHelper = New SqlHelper.SqlHelper() '声明并实例化SqlHelper类 '''
''' 调用SqlHelper把传来的用户基本信息添加到T_UserInfo表中 ''' ''' 用户信息实体类 '''返回执行 查询 得到的结果,为DataTable类型 Public Function AddUserInfo(ByVal enUserInfo As UserInfoEntity) As Integer Implements IDAL.IUserInfo.AddUserInfo '声明并实例化需要执行的SQL语句 Dim strSql As String = "insert into T_UserInfo (userName,password,realName,gmlevel,accountBy) values (@userName,@password,@realName,@gmlevel,@accountBy)" '声明并实例化参数数组 Dim sqlParams As SqlParameter() = {New SqlParameter("@userName", enUserInfo.UserName), New SqlParameter("@password", enUserInfo.Password), New SqlParameter("@realName", enUserInfo.RealName), New SqlParameter("@gmlevel", enUserInfo.Gmlevel), New SqlParameter("@accountBy", enUserInfo.AccountBy)} '调用SqlHelper类中的ExecAddDelUpdate()方法来执行添加信息,获取返回值并Return Return clsSqlHelper.ExecAddDelUpdate(strSql, CommandType.Text, sqlParams) End Function '''''' 删除用户信息 ''' ''' 用户信息实体类 '''返回执行 删除 语句受影响的行数,为Integer类型 '''Public Function DeleteUser(ByVal enUserInfo As UserInfoEntity) As Integer Implements IUserInfo.DeleteUser '声明并实例化需要执行的SQL语句 Dim strSql As String = "delete T_UserInfo where userName =@userName" '声明并实例化参数数组 Dim sqlParams As SqlParameter() = {New SqlParameter("@userName", enUserInfo.UserName)} '调用SqlHelper类中的ExecAddDelUpdate()方法来执行添加信息,获取返回值并Return Return clsSqlHelper.ExecAddDelUpdate(strSql, CommandType.Text, sqlParams) End Function ''' ''' 调用SqlHelper完成对T_UserInfo表中相就用户密码的悠 ''' ''' 用户信息实体类 '''返回执行 更新 语句受影响的行数,为Integer类型 '''Public Function UpdatePassword(ByVal enUserInfo As UserInfoEntity) As Integer Implements IUserInfo.UpdatePassword '声明并实例化需要执行的SQL语句 Dim strSql As String = "update T_UserInfo set password =@password where userName =@userName" '声明并实例化参数数组 Dim sqlParams As SqlParameter() = {New SqlParameter("@userName", enUserInfo.UserName), New SqlParameter("@password", enUserInfo.Password)} '调用SqlHelper类中的ExecAddDelUpdate()方法来执行添加信息,获取返回值并Return Return clsSqlHelper.ExecAddDelUpdate(strSql, CommandType.Text, sqlParams) End Function ''' ''' 根据用户名调用SqlHelper来查询T_UserInfo表中相应用户信息 ''' ''' 用户信息实体类 '''返回执行 查询 得到的结果,为DataTable类型 '''Public Function SelectUserInfo(ByVal enUserInfo As Entity.UserInfoEntity) As DataTable Implements IDAL.IUserInfo.SelectUserInfo Dim strUserName As String = enUserInfo.UserName '声明并实例化strUserName为实体中的用户名 Dim table As DataTable '声明一个DataTable类型变量 Dim strSQL As String = "select * from T_UserInfo where userName=@userName" '声明并实例化需要执行的SQL语句 Dim sqlParams As SqlParameter() = {New SqlParameter("@userName", strUserName)} '声明并实例化参数数组 table = clsSqlHelper.ExecSelect(strSQL, CommandType.Text, sqlParams) '调用SqlHelper类中的ExecSelect()方法来执行查询,并获取返回值 Return table '返回查询结果 End Function End Class
配置文件(之前的博客也提及了——机房收费系统总结之2——配置文件(初尝))内容如下:
抽象工厂中的代码如下:
Imports System.Configuration '添加对配置文件的引用 Imports System.Reflection '添加对反射的引用 Imports IDAL Public Class DataAccess Private strAssembly As String '程序集名 '''
''' 构造函数,通过调用配置文件为strAssembly赋初值 ''' Sub New() Me.strAssembly = ConfigurationManager.AppSettings("DB") & "DAL" End Sub '''''' 实例化接口为IUserInfo为UserInfoDAL ''' '''IUserInfo Public Function CreateUserInfoDAL() As IUserInfo Dim strInstance As String = strAssembly & "." & "UserInfoDAL" '所要实例化的对象(程序集与命名空间同名) Return CType(Assembly.Load(strAssembly).CreateInstance(strInstance), IUserInfo) '返回IUserInfo End Function End Class
在上面的代码中"DB"的值为使用的相应数据库的名字,在本程序中明明空间的名字和程序集的名字相同,所以没有灵性设置。通过将使用的数据库的名字用配置文件中key值来代替便可以十分方便的更换数据库。另外在上述代码中将实例化的D层类通过向上转型转换成接口类,然后通过调用接口类中的函数来调用D层中实现该接口的函数。
BLL层的类,我们可以根据功能来分,把与该功能相关的操作集成到一个BLL层的类里,这里我们要把握好粒度,平衡就好。尽量做到符合单一职责原则,一个类完成一个功能,即不要在BLL层出现类之间互相调用的情况,虽然可以减少代码量,但会增加系统的复杂性,造成模块与模块之间的强耦合。
下面仅以判断用记密码是否正确为例:
七、FaçadeImports Factory Imports IDAL Imports Entity Public Class ConditionBLL '声明并实例化factory为DataAccess类 Private ReadOnly factory As DataAccess = New DataAccess() '''
''' 判断用户密码是否正确:根据返回来的DataTable中的Password的属性值,与传来的所输入和密码对比,看密码是否输入正确 ''' ''' 用户信息实体类 '''如果密码不正确,抛出异常 Public Function IsPswRight(ByVal enUserinfo As UserInfoEntity) '声明并实例化变量interfaceUserInfo为:调用factory.CreateUserInfoDAL()方法所返回来的IUserInfo Dim interfaceUserInfo As IUserInfo = factory.CreateUserInfoDAL() '声明并实例化变量table为:通过传入的enUserInfo参数而调用interfaceUserInfo的SelectUserInfo(enUserInfo)方法所返回来的DataTable型数据 Dim table As DataTable = interfaceUserInfo.SelectUserInfo(enUserinfo) 'DataTable中的Password的属性值,与传来的所输入和密码对比,看密码是否输入正确,如果密码不正确,抛出异常 If table.Rows(0).Item("Password") <> enUserinfo.Password Then Throw New Exception("密码输入不正确") End If End Function End Class
Imports Entity Imports BLL Public Class LoginFacade Private bllIsExists As IsExistsBLL = New IsUserExistsBLL() '声明并实例化bllIsExists为IsUserExistsBLL类 Private bllCondition As ConditionBLL = New ConditionBLL() '声明并实例化bllCondition为ConditionBLL类 Private bllAddAboutUser As AddAboutUserBLL = New AddAboutUserBLL() '声明并实例化bllLogin为LoginBLL类 '''
''' 完成登录的一系列操作 ''' ''' 用户信息实休类 ''' 用户工作信息实体类 Public Function Login(ByVal enUserInfo As UserInfoEntity, ByVal enUserWork As UserWorkEntity) '判断用户是否存在 If bllIsExists.IsExists(enUserInfo) = 0 Then Throw New Exception("此用户不存在") End If bllCondition.IsPswRight(enUserInfo) '判断输入密码是否正确 bllCondition.IsUserOn(enUserWork) '判断用户是否已登录系统 bllAddAboutUser.AddUserWorkOn(enUserWork) '添加用户登录信息 End Function End Class
八、UI
U层负责数据的录入与输出,在U层,先通过调用U层中的验证用户类的验证方法来确认所输入用户是否合法,然后调用外观层中的方法进行判定,并完成登录。
Imports Facade Imports Entity Public Class frmLogin Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click Dim strComName As String = System.Net.Dns.GetHostName() '获取计算机名并赋值给变量strComName '给公共变量中的计算机名和用户名赋值 PublicEntity.ComName = strComName PublicEntity.UserName = txtUserName.Text.Trim() '定义一个UserInfoEntity类型的实体类变量,并分别把输入的用户名和密码赋值给变量的UserName属性和Password属性 Dim enUserInfo As New UserInfoEntity With {.UserName = txtUserName.Text.Trim(), .Password = txtPassword.Text.Trim()} '定义一个UserWorkEntity类型的实体类变量,并分别打输入的用户名和所获得的计算机名赋值给变量的UserName属性和ComName属性 Dim enUserWord As New UserWorkEntity With {.UserName = txtUserName.Text.Trim(), .ComName = strComName} '判断是否输入了用户名及密码 If txtUserName.Text.Trim() = "" Then MessageBox.Show("请输入用户名", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub ElseIf txtPassword.Text.Trim() = "" Then MessageBox.Show("请输入用密码", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If Try Dim facadeLogin As LoginFacade = New LoginFacade() '声明LoginFacade类型变量facadeLogin,并实例化 facadeLogin.Login(enUserInfo, enUserWord) '调用Login()方法,完成登录 Me.Hide() '隐藏登录窗体 frmMain.Show() '显示主窗体 Catch ex As Exception '捕获异常并显示 MsgBox(ex.Message, CType(vbOKOnly + MsgBoxStyle.Information, MsgBoxStyle), "提示") End Try End Sub End Class