注意:文中代码有误,为保留历史痕迹在本文不做修改,正确代码详
见《
纠错
》
在做系统的时候有意识的用到了抽象工厂这个设计模式,主要解决的是数据库更换的问题。
下面就以简单的登录来逐步的分析一下这个模式。
经典的三层架构
数据库如下
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
业务逻辑层
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
数据持久层
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
2.
简单工厂
添加一个工厂类和一个接口
接口类
1:
Public
Interface
IUserInfo
2:
Function
Check(
ByVal
IUser
As
Entity.User)
As
Entity.User
3:
End
Interface
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
工厂类
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style> <style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
当然
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
数据持久层
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
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
配置文件
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
>
<style type="text/css"> <!-- .csharpcode, .csharpcode pre {font-size:small; color:black; font-family:consolas,"Courier New",courier,monospace; background-color:#ffffff} .csharpcode pre {margin:0em} .csharpcode .rem {color:#008000} .csharpcode .kwrd {color:#0000ff} .csharpcode .str {color:#006080} .csharpcode .op {color:#0000c0} .csharpcode .preproc {color:#cc6633} .csharpcode .asp {background-color:#ffff00} .csharpcode .html {color:#800000} .csharpcode .attr {color:#ff0000} .csharpcode .alt {background-color:#f4f4f4; width:100%; margin:0em} .csharpcode .lnum {color:#606060} --> </style>
添加两个
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
。
三层架构之抽象工厂加反射----实现数据库转换