三层架构之抽象工厂加反射----实现数据库转换

注意:文中代码有误,为保留历史痕迹在本文不做修改,正确代码详见《纠错

在做系统的时候有意识的用到了抽象工厂这个设计模式,主要解决的是数据库更换的问题。

下面就以简单的登录来逐步的分析一下这个模式。

经典的三层架构

image

数据库如下

image

1.      一般的数据库连接方式

界面层

image

 
   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.      简单工厂

image

添加一个工厂类和一个接口

接口类

   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.抽象工厂加反射

image

工厂类

   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

posted @ 2011-03-25 10:23  郗晓勇  阅读(371)  评论(0编辑  收藏  举报