在做系统的时候有意识的用到了抽象工厂这个设计模式,主要解决的是数据库更换的问题。
下面就以简单的登录来逐步的分析一下这个模式。
经典的三层架构
数据库如下
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 = DaUser.Check(User)
6:
If
BlUser.User_Pwd = User.User_Pwd
Then
7:
Return
True
8:
Else
9:
Return
False
10:
End
If
11:
End
Function
12:
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 。

