三层架构之抽象工厂加反射----实现数据库转换
注意:文中代码有误,为保留历史痕迹在本文不做修改,正确代码详见《纠错》
在做系统的时候有意识的用到了抽象工厂这个设计模式,主要解决的是数据库更换的问题。
下面就以简单的登录来逐步的分析一下这个模式。
经典的三层架构
数据库如下
1. 一般的数据库连接方式
界面层
1: Public Class Login
2: Private Sub btnLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLogin.Click
3: Dim LUser As New Entity.User
4: Dim BCheck As New BLL.B_Login
5: LUser.User_ID = txtName.Text
6: LUser.User_Pwd = txtPwd.Text
7: If BCheck.Check(LUser) = True Then
8: MsgBox("登录成功!")
9: Else
10: MsgBox(“"登录失败!")
11: End If
12: End Sub
13:
14: Private Sub btnCancle_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancle.Click
15: End
16: End Sub
17: End Class
业务逻辑层
1: Public Class B_Login
2: Function Check(ByVal User As Entity.User) As Boolean
3: Dim DaUser As New DAL.D_UserInfo
4: Dim BlUser As New Entity.User
5: Bluser.User_ID=User.ID
6: BlUser = DaUser.Check(BlUser)
7: If BlUser.User_Pwd = User.User_Pwd Then
8: Return True
9: Else
10: Return False
11: End If
12: End Function
13: End Class
数据持久层
1: Imports System.Data.SqlClient
2: Public Class D_UserInfo
3: Dim ConnStr As String = "Data Source=******;Initial Catalog=Student;User ID=sa;Password=******"
4: Dim conn As SqlConnection = New SqlConnection(ConnStr)
5: Function Check(ByVal User As Entity.User) As Entity.User
6: Dim sql As String = "select * from UserInfo where UserInfo=" & User.User_ID
7: Dim cmd As SqlCommand = New SqlCommand(sql, conn)
8: Dim read As SqlDataReader
9: Try
10: conn.Open()
11: read = cmd.ExecuteReader
12: User.User_ID = read.Item(0)
13: User.User_Pwd = read.Item(1)
14: Return User
15: Catch ex As Exception
16: User.User_Pwd = ""
17: Return User
18: End Try
19: End Function
20: End Class
2. 简单工厂
添加一个工厂类和一个接口
接口类
1: Public Interface IUserInfo
2: Function Check(ByVal IUser As Entity.User) As Entity.User
3: End Interface
工厂类
1: Imports [Interface]
2: Public Class DFactory
3: 'Dim DataBase As String = "Access"
4: Dim DataBase As String = "Sql"
5: Function CreateUserInfo() As IUserInfo
6: Dim DB As IUserInfo
7: Select Case DataBase
8: Case "Sql"
9: DB = New D_UserInfoSql
10: 'Case "Access"
11: ' DB = New D_UserInfoAccess
12: End Select
13: Return DB
14: End Function
15: End Class
当然D_UserInfoSql实现接口,代码基本不变
当有新的数据库使用时候(例如Access数据库)可以将工厂中的注释部分添上,然后重新写Dal层就可以直接使用,但是这样的不足是还是需要再次编译工厂,利用反射可以解决这个问题。
3.抽象工厂加反射
工厂类
1: Imports [Interface]
2: Imports System.Reflection
3: Public Class DFactory
4: '抽象工厂加反射
5: Dim DBString As String = System.Configuration.ConfigurationSettings.AppSettings("DBString")
6: Function CreateUserInfo() As IUserInfo
7: Return CType(Assembly.Load("DAL").CreateInstance("DAL.D_UserInfo" & DBString), IUserInfo)
8: End Function
9: End Class
数据持久层
1: Imports System.Data.SqlClient
2: Public Class D_UserInfoSql : Implements [Interface].IUserInfo
3: 'Dim ConnStr As String = "Data Source=******;Initial Catalog=Student;User ID=sa;Password=******"
4: Dim ConnStr As String = System.Configuration.ConfigurationSettings.AppSettings("ConnStr")
5: Dim conn As SqlConnection = New SqlConnection(ConnStr)
6: Public Function Check(ByVal IUser As Entity.User) As Entity.User Implements [Interface].IUserInfo.Check
7: Dim sql As String = "select * from UserInfo where UserInfo=" & IUser.User_ID
8: Dim cmd As SqlCommand = New SqlCommand(sql, conn)
9: Dim read As SqlDataReader
10: Try
11: conn.Open()
12: read = cmd.ExecuteReader
13: IUser.User_ID = read.Item(0)
14: IUser.User_Pwd = read.Item(1)
15: Return IUser
16: Catch ex As Exception
17: IUser.User_Pwd = ""
18: Return IUser
19: End Try
20: End Function
21: End Class
配置文件
1: <appSettings>
2: <add key="ConnStr" value ="Data Source=******;Initial Catalog=Student;User ID=sa;Password=******"></add>
3: <add key="DBString" value ="Sql"></add>
4: </appSettings>
添加两个Key,一个是连接数据库的字符串,一个是通过反射来产生不同数据库的Dal层的
这样一来就可以实现设计模式中的“开闭原则”,如果更换数据库只需要增加类(DAL),而不需要更改,更不需要重新编译。
PS:配置文件必须在界面层
1、 反射的写法:
objType=Assembly.Load(AssemblyPath).CreateInstance(className);其中:AssemblyPath指程序集名。className指命名空间.类名称。
2、 反射的一个原则:一切皆以UI层的bin文件夹中的dll名称为中心。(原因很简单:.net类加载的机制就是默认从本程序集的bin文件中找,所以bin文件夹中一定要有要加载的程序集的dll)。UI层中bin文件夹中dll叫什么名字AssemblyPath就使用什么名字,bin内部类的全名叫什么,className就写成什么全名。.net中的引用:加入对某个程序集的引用就能在程序集有变化时自动拷贝dll。