机房收费系统之抽象工厂篇
机房收费系统vb.net个人版已完成,在此过程中分层的好处在系统的完成过程中得到了很好的体会。第一遍用的是纯三层做的,这几天又在此基础上又重构了登录的demo,加上抽象工厂和配置文件的使用。下面以登录功能为例,总结一下我的机房收费系统。
一、下面看一下我的包图:
可以看到,这张图是从最基本的三层UI→BLL→DAL加入设计模式(抽象工厂)演化而来的,采用抽象工厂是考虑到日后更换数据库的方便。
二、准备工作
创建数据表(课参见实体类中的代码)
创建相应的类库和窗体。
我用的是vb.net的WidowsApplication来添加界面;使用类库作为其它层的包。
三、抽象各层的类
当我们确定了系统的整体架构,就要从宏观到微观的实现。
3.1 Entity(实体层)
数据库设计好了,我们要根据数据库中的表抽象实体类,系统中,实体类和表基本上是一一对象的。一个表映射一个实体类,表的字段即为实体类的属性。
实体层并不属于三层的任何一层,它是独立出来的一层,可以把它看做是自定义变量的组合,供其它三层使用。
Public Class UserInfoEntity
#Region "定义UserInfo表中各个属性变量"
Private _userName As String
Private _name As String
Private _password As String
Private _userlevel As String
Private _accountHolder As String
Private _regDate As String
Private _regTime As String
#End Region
#Region "定义数据表中各个字段名常量"
Private Const DBFLELD_USERNAME = "userName"
Private Const DBFLELD_NAME = "name"
Private Const DBFLELD_PASSWORD = "password"
Private Const DBFLELD_USERLEVEL = "userLevel"
Private Const DBFLELD_ACCOUNTHOLDER = "accountHolder"
Private Const DBFLELD_REGDATE = "regDate"
Private Const DBFLELD_TIME = "regTime"
#End Region
#Region "定义数据表中各个字段参数变量"
Public Const DBPARAM_USERNAME = "@userName"
Public Const DBPARAM_NAME = "@name"
Public Const DBPARAM_PASSWORD = "@password"
Public Const DBPARAM_USERLEVEL = "@userLevel"
Public Const DBPARAM_ACCOUNTHOLDER = "@accountHolder"
Public Const DBPARAM_REGDATE = "@regDate"
Public Const DBPARAM_TIME = "@regTime"
#End Region
'''
''' 获取数据表中的一条记录
'''
''' 字段名
''' 2013-5-9 by liuhaiyan
Public Sub LoadFromDataRow(ByVal dr As DataRow)
userName = dr(DBFLELD_USERNAME)
name = dr(DBFLELD_NAME)
password = dr(DBFLELD_PASSWORD)
userlevel = dr(DBFLELD_USERLEVEL)
accountHolder = dr(DBFLELD_ACCOUNTHOLDER)
regDate = dr(DBFLELD_REGDATE)
regTime = dr(DBFLELD_TIME)
End Sub
#Region "填充一条记录"
'''
''' 填充一条记录(登录)到实体层
'''
''' 用户名
''' 密码
''' 用户级别
''' 2013-5-9 by liu hai yan
Public Overloads Sub Fill(ByVal AuserName As String, ByVal Apassword As String, ByVal AuserLevel As String)
userName = AuserName
password = Apassword
userlevel = AuserLevel
End Sub
#End Region
#Region "定义数据表中各个属性"
'''
''' 用户名
'''
'''
'''
'''
Public Property userName() As String
Get
Return _userName
End Get
Set(ByVal value As String)
_userName = value
End Set
End Property
'''
''' 用户姓名
'''
'''
'''
'''
Public Property name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
'''
''' 用户密码
'''
'''
'''
'''
Public Property password() As String
Get
Return _password
End Get
Set(ByVal value As String)
_password = value
End Set
End Property
'''
''' 用户权限
'''
'''
'''
'''
Public Property userlevel() As String
Get
Return _userlevel
End Get
Set(ByVal value As String)
_userlevel = value
End Set
End Property
'''
''' 开户人
'''
'''
'''
'''
Public Property accountHolder() As String
Get
Return _accountHolder
End Get
Set(ByVal value As String)
_accountHolder = value
End Set
End Property
'''
''' 注册日期
'''
'''
'''
'''
Public Property regDate() As String
Get
Return _regDate
End Get
Set(ByVal value As String)
_regDate = value
End Set
End Property
'''
''' 注册时间
'''
'''
'''
'''
Public Property regTime() As String
Get
Return _regTime
End Get
Set(ByVal value As String)
_regTime = value
End Set
End Property
#End Region
End Class
下面是配置文件的使用:在窗体层添加新项目→选中打开窗口的左边的General选项→添加配置文件,(中间部分是需要手动添加的)。
3.2 DAL层(数据访问层)
接下来我们再看一下数据访问层(DAL),这一层的主要任务是直接操作数据库,完成对数据的增删改查等。这里我们仍然根据数据表来抽象DAL层的类,基本上也是一个表对应一个类,这样当我们增加新的表,直接增加新的DAL层类就可以,很好地符合了“开闭原则”。
另外,因为DAL层的类是直接对数据库进行操作的类,所以这个类里封转大都有四种方法:增删改查。但根据实际情况会有不同的参数,不同的返回值。
这里添加了接口层IDAL,利用反射和抽象工厂,以防更换数据库。
3.2.1 接口层IDAL,添加Entity的引用。
Imports Entity
Public Interface Iuser
'''
''' 查看一条记录是否存在
'''
''' 用户信息
''' 一条用户记录
''' 2013-5-9 by liuhaiyan
Function Check(ByVal entityUser As UserInfoEntity) As UserInfoEntity
End Interface
3.2.2 DAL层实现IDAL接口
下面是DAL层下的DataAccess类
添加Configuration引用时需从.net子项目中查找,还要添加项目引用Entity和IDAL.
'如果没有使用配置文件加反射,不用引用Configuration和Reflection
Imports System.Configuration
Imports System.Reflection
Imports System.Data.SqlClient
Imports Entity
Imports IDAL
Public Class DataAccess
#Region "连接数据库"
Private ReadOnly connStr As String = ConfigurationManager.AppSettings("connStr")
'''
''' 创建一个数据库连接
'''
'''
''' 2013-5-9 by liuhaiyan
Public Overloads Function CreateConn() As SqlConnection
Return New SqlConnection(connStr)
End Function
#End Region
#Region "关闭相关对象"
'''
''' 关闭SqlConnection对象
'''
''' 连接数据库
''' 2013-5-9 by liuhaiyan
Public Sub Close(ByVal conn As SqlConnection)
conn.Close()
conn = Nothing
End Sub
'''
''' 关闭SqlCommand对象
'''
''' sql命令
''' 2013-5-9 by liuhaiyan
Public Sub Close(ByVal cmd As SqlCommand)
cmd.Dispose()
cmd = Nothing
End Sub
#End Region
'''
''' 为sql变量赋值并添加到SqlCommand中
'''
''' SqlCommand命令
''' 参数名
''' 参数类型
''' 参数值
''' 2013-5-9 by liuhaiyan
Public Sub AddSqlParameter(ByVal cmd As SqlCommand, ByVal dbParam As String, ByVal dbType As SqlDbType, ByVal value As Object)
Dim sqlParam As SqlParameter = New SqlParameter(dbParam, dbType)
sqlParam.Value = value
cmd.Parameters.Add(sqlParam)
End Sub
End Class
DAL层下的DBuser类:
同样添加项目引用Entity和IDAL.
Imports System.Data.SqlClient
Imports Entity
Imports IDAL
Public Class DBuser
'前面BLL没有用抽象工厂的这里不用继承DataAccess
Inherits DataAccess
Implements Iuser
Public Function Check(ByVal entityUser As Entity.UserInfoEntity) As UserInfoEntity Implements IDAL.Iuser.Check
'这里的sql语句是为了以后的灵活性,将各个字段使用了常量来进行表示,具体可参见实体层
Dim sql As String = String.Format("select * from userInfo where [userName]={0} and [password]={1} and [userLevel]={2}", UserInfoEntity.DBPARAM_USERNAME, UserInfoEntity.DBPARAM_PASSWORD, UserInfoEntity.DBPARAM_USERLEVEL)
Dim conn As SqlConnection = CreateConn()
Dim cmd As SqlCommand = New SqlCommand(sql, conn)
Dim sda As SqlDataAdapter = New SqlDataAdapter(cmd)
Dim ds As New DataSet
'向cmd中添加参数
AddSqlParameter(cmd, UserInfoEntity.DBPARAM_USERNAME, SqlDbType.VarChar, entityUser.userName)
AddSqlParameter(cmd, UserInfoEntity.DBPARAM_PASSWORD, SqlDbType.VarChar, entityUser.password)
AddSqlParameter(cmd, UserInfoEntity.DBPARAM_USERLEVEL, SqlDbType.VarChar, entityUser.userlevel)
Try
conn.Open()
sda.Fill(ds, "userInfo")
Dim dr As DataRow = ds.Tables("userInfo").Rows(0)
entityUser.LoadFromDataRow(dr)
Return entityUser
Catch ex As Exception
Return Nothing
Finally
Close(cmd)
Close(conn)
End Try
End Function
End Class
抽象工厂+反射+配置文件
Imports System.Configuration
Imports System.Reflection
Imports IDAL
Public Class Factory
#Region "配置加反射"
Private Shared ReadOnly AssemblyName As String = ConfigurationManager.AppSettings("AssemblyName")
Private Shared ReadOnly db As String = ConfigurationManager.AppSettings("db")
'''
''' 实例化Iuser类型的DBuser类对象
'''
''' DBuser类对象
''' 2013-5-9 by liuhaiyan
Public Shared Function CreateUser() As Iuser
Dim ClassName As String
ClassName = AssemblyName + "." + db + "user"
Return CType(Assembly.Load(AssemblyName).CreateInstance(ClassName), Iuser)
End Function
#End Region
End Class
3.BLL层(封装业务层):前提添加DAL,IDAL和Entity的引用
Imports IDAL
Imports Entity
Imports Factory
Public Class BllLogin
'''
''' 查看登录信息是否存在
'''
''' 窗体输入的用户信息
''' 登录是否成功
''' 2013-5-9 by liuhaiyan
Public Function CheckRecord(ByVal entityUserInfo As UserInfoEntity) As Boolean
Dim checkResult As Boolean
'下面声明的iuser是实例化的Iuser类型的DBuser,实现了接口的调用(接口虽然不能被实例化,但是继承它的类可实例化成接口类型)
Dim iuser As Iuser
'此处运用的是抽象工厂设计模式中的反射加配置,如果没有用设计模式可以讲下面的这句话改为:iuser=new DBuser()
iuser = Factory.Factory.CreateUser()
If Not IsNothing(iuser.Check(entityUserInfo)) Then
checkResult = True
Else
checkResult = False
End If
Return checkResult
End Function
End Class
4.UI层(界面层)
界面层的类,就是我们窗体类,又多少个 窗体,UI层就有多少个类。
下面我们看一下登录窗体类。
Imports BLL
Imports Entity
Public Class frmLogin
'登录
Private Sub btnLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLogin.Click
'判断文本框是否为空
If txtUserID.Text = "" Then
MessageBox.Show("用户名不能为空!")
txtUserID.Focus()
Return
End If
If txtPassword.Text = "" Then
MessageBox.Show("用户名不能为空!")
txtPassword.Focus()
Return
End If
'填充参数记录(此处调用的是实体层中实体类userInfo中的填充记录Fill方法,后面会有介绍)
Dim entityUserInfo As New UserInfoEntity
entityUserInfo.Fill(Trim(txtUserID.Text), Trim(txtPassword.Text), Trim(cboUserLevel.Text))
'验证用户记录(调用BLL层中的验证用户记录CheckRecord方法)
Dim bllUserInfo As New BllLogin
If bllUserInfo.CheckRecord(entityUserInfo) Then
Me.Hide()
frmMain.Show()
'判断用户级别
' ……
'添加用户记录
' ……
Else
MsgBox("登录失败", vbOKOnly + vbInformation, "登录失败")
txtUserID.Focus()
Exit Sub
End If
End Sub
'取消
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Me.Close()
End Sub
End Class
上面就是我在三层(UI→BLL→DAL)的基础上加上设计模式抽象工程实现系统登录的Demo,拿出来和大家一起分享,其中的不足之处,还希望大家多多指正。