机房收费系统之重新设计
这段时间,一直在实践一个小型的mis系统。像这种小型的mis系统,对数据库的操作无非就是增删改查。在做的时候,就在考虑一个问题,实现这样的系统,是否有一个比较通用的模式。
我们都知道面向对象,要想实现低耦合,接口起了很大的作用。而接口最主要的作用则是实现一种多态。而要实现多态,并不是给方法加个接口,然后实现这么简单。他还需要有一个类,见接口浅谈,程序因为有了AnimalBark类,才体现出了他的优势。很好的将多态的优点表示出来。技术因需要而变得重要,一个程序实现它的灵活性确实很重要,但也要由实际情况而定。如果一个需求,他确实变动的可能比较大,或是实践方式比较多,这时候,用多态确实很好。可是如果,它的变动不是很大的话,用多态除了复杂,并不能体现它的什么好处。而且,一个事物都有相当于都没有。只有需要的地方有,才能体现它的优点与好处。
说一下我这次的设计,这次的考虑,尽可能的减少提供给外部的接口,给有相似功能的行为提供一个对外接口。尽可能的让一个接口承担更多的功能,提高其内聚性。
看一下实例:
我们都知道mvc的三层结果。界面层,控制层,数据层。在这里我重点设计的是控制层与数据层的接口。
先看一下包图:
在这里,控制层通过工厂与数据层进行打交道。而工厂提供给控制层的只有Insert,Delete,Modify,Find四个接口。工厂通过传进来实体类的类型,来确定你到底要实例化的是哪个数据层的接口类。具体实现见方法SqlObjectCreate。(这里还有个小技巧,实体类和数据层类后缀都是一样的。如EnRegist,SqlRegist;EnUser,SqlUser)
1 ''''''''''''''''''''''''''''''''''''''
2 '工厂类,数据层与bll层的接口。b层的所有方法
3 '都必须通过此工厂的四个方法去调用数据层具体的接口
4 '12:24 5/8 5
'''''''''''''''''''''''''''''''''''''
6 Imports Entity
7 Imports System.Configuration
8 Imports IDAL
9 Imports System.Reflection
10
11 Public Class dalFactory
12 Private db As String '连接数据库的类型
13 Private className As String '要实例化的具体的类名称
14 Private AssemblyName As String '程序集名称
15 ''' <summary>
16 ''' 所有程序调用数据库添加操作的入口
17 ''' </summary>
18 ''' <param name="enObject">实体类参数</param>
19 ''' <returns>是否添加成功</returns>
20 ''' <remarks>5/8 12:19</remarks>
21 Public Function Insert(ByVal enObject As Object) As Boolean
22 Return objCreate(enObject).Add(enObject)
23 End Function
24 ''' <summary>
25 ''' 所有程序调用数据库删除操作的入口
26 ''' </summary>
27 ''' <param name="enObject">实体类参数</param>
28 ''' <returns>是否删除成功</returns>
29 ''' <remarks>5/8 12:20</remarks> 3
0 Public Function Delete(ByVal enObject As Object) As Boolean
31 Return objCreate(enObject).delete(enObject)
32 End Function
33 ''' <summary>
34 ''' 所有程序调用数据库修改操作的接口
35 ''' </summary>
36 ''' <param name="enObject">实体类参数</param>
37 ''' <returns>是否修改成功</returns>
38 ''' <remarks>5/8 12:21</remarks>
39 Public Function Modify(ByVal enObject As Object) As Boolean
40 Return objCreate(enObject).modify(enObject)
41 End Function
42 ''' <summary>
43 ''' 所有程序调用数据库查询操作的接口
44 ''' </summary>
45 ''' <param name="intNum">调用的是哪种类型的查询
46 ''' user 查询类型,1按级别查询该级别对应的记录2按用户名查询
47 ''' basicdata 1 取得基本数据,表中就一条基本数据
48 ''' charge
49 ''' 1按卡号查询
50 ''' 2按时间段查询
51 ''' 3按操作员查询未结账的信息
52 ''' 4按操作员查询未结账的充值总额
53 ''' line
54 ''' 1按卡号查询上机信息
55 ''' 2查找所有正在上机的学生信息
56 ''' 3操作员未结账的上机消费总额
57 ''' operate 查询所有正在上机的操作员信息
58 ''' order 按时间段查询
59 ''' regist 1按卡号进行查询
60 ''' 2按操作员进行查询
61 ''' return 1按时间段查询
62 ''' 2 操作员未结账的退卡信息查询
63 ''' 3操作员未结账退卡总金额查询
64 ''' </param>
65 ''' <param name="enObjectStart">起始参数</param>
66 ''' <param name="enObjectEnd">终止参数</param>
67 ''' <returns>查询到的结果</returns>
68 ''' <remarks>5/8 12:22</remarks>
69 Public Function Find(ByVal intNum As Integer, ByVal enObjectStart As Object, ByVal enObjectEnd As Object) As DataTable
70 Return objCreate(enObjectStart).find(intNum, enObjectStart, enObjectEnd)
71 End Function
72 ''' <summary>
73 ''' 所有要实例化类的入口。根据传进去参数所属类型的不同,实例化不同的类对象
74 ''' </summary>
75 ''' <param name="enObject">实体类</param>
76 ''' <returns>具体实例化的数据层类的对象</returns>
77 ''' <remarks>5/8 11:36</remarks>
78 Private Function objCreate(ByRef enObject As Object) As Object
79 Dim strType As String '用来接收传进来对象的类型,以判断到底要调用哪个类的数据层
80 strType = enObject.GetType.ToString
81 Return sqlObjectCreate(strType)
82 End Function
83 ''' <summary>
84 ''' 实例化具体数据层类,输入类型,输出该类型对应的应实例化的类
85 ''' 这里有个技巧,数据层与实体类实例化对象除了前缀不同的,其他的地都是相同的。
86 ''' </summary>
87 ''' <returns>实例化后的类</returns>
88 ''' <remarks>5/8 11:34</remarks>
89 Private Function sqlObjectCreate(ByVal strType As String) As Object
90 db = System.Configuration.ConfigurationManager.AppSettings("DB") '取得连接字符串
91 AssemblyName = "DAL"
92 Dim strTemp As String '用来判定具体应该返回那种类型的实例
93 strTemp = Mid(strType, 10, strType.Length - 9) '获取实体与数据层公共的部分
94 '具体实例化类的名字。取得所传过来变量的后几位,即把前缀给去掉。如entity.Regist变成Regist
95 className = AssemblyName + "." + db + Mid(strType, 10, strType.Length - 9)
96 Dim objReturn As New Object '用来取得具体的返回类型实例
97 '由实体类与数据层的公共部分,判定到底返回的是哪种类型的对象
98 Select Case strTemp
99 Case "User"
100 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IUser)
101 'objReturn = Assembly.Load(AssemblyName).CreateInstance(className)
102 Case "BasicData"
103 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IBasicData)104 Case "Charge"
105 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), ICharge)
106 Case "Line"
107 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), ILine)
108 Case "OperateWork"
109 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IOperateWork)
110 Case "Order"
111 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IOrder)
112 Case "Regist"
113 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IRegist)
114 Case "Return"
115 objReturn = CType(Assembly.Load(AssemblyName).CreateInstance(className), IReturn)
116 End Select
117 Return objReturn
118 'Return New classname
119 End Function
120 End Class
可能你会有疑问,查询到方式多种多样,一个接口能解决这个问题吗,可以看一下Find方法的注释,由于我们先能够确定出具体是哪个数据层,然后进行调用的时候,只需要知道那个数据层Find方法具体是怎么实现的就可以了。这里通过前面一个int型参数来确定具体要调用的是那种查询方式。
通过一个具体的过程来看一下这个程序是如何走的。
看一个查询。界面
1 ''''''''''''''''''''''''''''''''''
2 '按卡号查找充值信息
3 '23:24 5/9
4 ''''''''''''''''''''''''''''''
5 Imports BLL
6 Public Class frmChargeCheck
7 Private Sub btnCheck_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCheck.Click
8 Dim bllCharge As New bllCharge '实例化充值控制层
9 '将查询到的结果与控件进行绑定
10 dgvContent.DataSource = bllCharge.findByCardNo(txtCardNo.Text)
11 End Sub
12 End Class
控制层
1 ''''''''''''''''''''''''''
2 '充值控制层
3 '5/8 21:13
4 ''''''''''''''''''''''
5 Imports Entity
6 Public Class bllCharge
7 Private dalFactory As dalFactory.dalFactory '定义工厂类变量
8 ''' <summary>
9 ''' 按卡号查找充值信息
10 ''' </summary>
11 ''' <param name="strCardNo">卡号</param>
12 ''' <returns>该卡号对应的充值信息</returns>
13 ''' <remarks></remarks>
14 Public Function findByCardNo(ByVal strCardNo As String) As DataTable
15 dalFactory = New dalFactory.dalFactory '实例化工厂类
16 Dim enCharge As New enCharge '用来传递卡号
17 enCharge.CardNo = strCardNo '给其装载卡号
18 '返回查询的结果
19 Return dalFactory.Find(1, enCharge, enCharge)
20 End Function
21 End Class
看一下充值接口层(这里显示他实际所拥有的功能)
1 '''''''''''''''''''''''
2 '充值数据层接口
3 '5/8 17:01
4 '''''''''''''''''''''''
5 Imports Entity
6 Public Interface ICharge
7 ''' <summary>
8 ''' 充值,向充值记录中添加记录
9 ''' </summary>
10 ''' <param name="enCharge">充值记录,除结账状态,其他属性都以赋值</param>
11 ''' <returns>是否充值成功</returns>
12 ''' <remarks></remarks>
13 Function Add(ByVal enCharge As enCharge) As Boolean
14 ''' <summary>
15 ''' 查询总入口
16 ''' </summary>
17 ''' <param name="intNum">
18 ''' 查询类型变量
19 ''' 1按卡号查询
20 ''' 2按时间段查询
21 ''' 3按操作员查询未结账的信息
22 ''' 4按操作员查询未结账的充值总额
23 ''' </param>
24 ''' <param name="enChargeStart">开始记录</param>
25 ''' <param name="enChargeEnd">结束记录</param>
26 ''' <returns>查询到的充值信息</returns>
27 ''' <remarks></remarks>
28 Function Find(ByVal intNum As Integer, ByVal enChargeStart As enCharge, ByVal enChargeEnd As enCharge) As DataTable
29 End Interface
具体的数据层,通过它你可以看到实际上它有多种查询方式,可是对外界开发的接口却只有一个Find
1 '''''''''''''''''''''''
2 '充值数据层
3 '13:55 5/8
4 '''''''''''''''''''''''
5 Imports IDAL
6 Imports Entity
7 Imports System.Data.SqlClient
8 Public Class SqlCharge
9 Implements ICharge
10 ''' <summary>
11 ''' 将sqlhelper类实例化
12 ''' </summary>
13 ''' <remarks></remarks>
14 Private sqlHelper As SQLHelper
15 ''' <summary>
16 ''' 要执行的sql字符串
17 ''' </summary>
18 ''' <remarks></remarks>
19 Private strSql As String
20 ''' <summary>
21 ''' 实例化的command对象
22 ''' </summary>
23 ''' <remarks></remarks>
24 Private cmd As SqlCommand
25 ''' <summary>
26 ''' 用来接收执行结果是否成功
27 ''' </summary>
28 ''' <remarks></remarks>
29 Private blResult As Boolean
30 ''' <summary>
31 ''' 充值,向充值记录中添加记录
32 ''' </summary>
33 ''' <param name="enCharge">充值记录,除结账状态,其他属性都以赋值</param>
34 ''' <returns>是否充值成功</returns>
35 ''' <remarks></remarks>
36 Public Function Add(ByVal enCharge As Entity.enCharge) As Boolean Implements IDAL.ICharge.Add
37 sqlHelper = New SQLHelper()
38 strSql = "procCharge"
39 cmd = New SqlCommand(strSql, sqlHelper.conGet())
40 '指定cmd类型,用存储过程
41 cmd.CommandType = CommandType.StoredProcedure
42 '给command对象添加参数
43 sqlHelper.SqlParameterAdd(cmd, "@CardNo", SqlDbType.VarChar, enCharge.CardNo)
44 sqlHelper.SqlParameterAdd(cmd, "@ChargeTime", SqlDbType.DateTime, enCharge.ChargeTime)
45 sqlHelper.SqlParameterAdd(cmd, "@OperatorName", SqlDbType.VarChar, enCharge.OperatorName) 46 sqlHelper.SqlParameterAdd(cmd, "@Charge", SqlDbType.Money, enCharge.Charge)
47 sqlHelper.SqlParameterAdd(cmd, "@LastBalance", SqlDbType.Money, enCharge.LastBalance)
48 sqlHelper.SqlParameterAdd(cmd, "@CurrentBalance", SqlDbType.Money, enCharge.CurrentBalance) 49 '执行,并将结果返回给blresult
50 blResult = sqlHelper.ExecuteNonQuery(strSql, cmd)
51 Return blResult
52 End Function
53 ''' <summary>
54 ''' 查询总入口
55 ''' </summary>
56 ''' <param name="intNum">
57 ''' 查询类型变量
58 ''' 1按卡号查询
59 ''' 2按时间段查询
60 ''' 3按操作员查询未结账的信息
61 ''' 4按操作员查询未结账的充值总额
62 ''' </param>
63 ''' <param name="enChargeStart">开始记录</param>
64 ''' <param name="enChargeEnd">结束记录</param>
65 ''' <returns>查询到的充值信息</returns>
66 ''' <remarks></remarks>
67 Public Function Find(ByVal intNum As Integer, ByVal enChargeStart As Entity.enCharge, ByVal enChargeEnd As Entity.enCharge) As System.Data.DataTable Implements IDAL.ICharge.Find
68 Dim dt As New DataTable '用来接收返回的结果
69 Try
70 Select Case intNum
71 '按卡号查找充值信息
72 Case 1
73 dt = findByCardNo(enChargeStart)
74 '按时间段查找充值信息
75 Case 2
76 dt = findByTime(enChargeStart, enChargeEnd)
77 Case 3
78 '按操作员查找未结账的充值信息
79 dt = findByOperator(enChargeStart)
80 Case 4
81 '按操作员查找未结账的充值总额
82 dt = chargeSumFindByOper(enChargeStart)
83 End Select
84 Catch ex As Exception
85 dt = Nothing
86 End Try
87 Return dt
88 End Function
89 ''' <summary>
90 ''' 按卡号查找充值信息
91 ''' </summary>
92 ''' <param name="enCharge">充值记录,只包含卡号属性就可以</param>
93 ''' <returns>该卡号对应的充值信息</returns>
94 ''' <remarks></remarks>
95 Private Function findByCardNo(ByVal enCharge As enCharge) As DataTable
96 sqlHelper = New SQLHelper()
97 Dim dt As New DataTable '用来接收查询的结果
98 '给sql语句赋值
99 strSql = "select * from Charge where CardNo=@CardNo"
100 cmd = New SqlCommand(strSql, sqlHelper.conGet())
101 sqlHelper.SqlParameterAdd(cmd, "@CardNo", SqlDbType.VarChar, enCharge.CardNo)
102 dt = sqlHelper.ExecuteQuery(strSql, cmd)
103 Return dt
104 End Function
105 ''' <summary>
106 ''' 按时间段查找充值信息
107 ''' </summary>
108 ''' <param name="enChargeStart">充值的开始时间,只要时间就行</param>
109 ''' <param name="enChargeEnd">充值的结束时间,只要时间就可以</param>
110 ''' <returns>该时间段内的充值信息</returns>
111 ''' <remarks></remarks>
112 Private Function findByTime(ByVal enChargeStart As enCharge, ByVal enChargeEnd As enCharge) As DataTable
113 sqlHelper = New SQLHelper()
114 Dim dt As New DataTable '用来接收查询到 结果
115 strSql = "select * from Charge where ChargeTime between @start and @end"
116 cmd = New SqlCommand(strSql, sqlHelper.conGet())
117 '添加参数
118 sqlHelper.SqlParameterAdd(cmd, "@start", SqlDbType.Date, enChargeStart.ChargeTime)
119 sqlHelper.SqlParameterAdd(cmd, "@end", SqlDbType.Date, enChargeEnd.ChargeTime)
120 '接收查询到的结果
121 dt = sqlHelper.ExecuteQuery(strSql, cmd)
122 Return dt
123 End Function
124 ''' <summary>
125 ''' 该操作员未结账的充值信息
126 ''' </summary>
127 ''' <param name="enCharge">充值记录,只包含操作员就行</param>
128 ''' <returns>该操作员未结账的充值信息</returns>
129 ''' <remarks></remarks>
130 Private Function findByOperator(ByVal enCharge As enCharge) As DataTable
131 sqlHelper = New SQLHelper()
132 Dim dt As New DataTable '用来接收查询的结果
133 '给sql语句赋值
134 strSql = "select * from Charge where OperatorName=@Oper and OrderState='未结账'"
135 cmd = New SqlCommand(strSql, sqlHelper.conGet())
136 sqlHelper.SqlParameterAdd(cmd, "@Oper", SqlDbType.VarChar, enCharge.OperatorName)
137 dt = sqlHelper.ExecuteQuery(strSql, cmd)
138 Return dt
139 End Function
140 ''' <summary>
141 ''' 该操作员未结账的充值总额
142 ''' </summary>
143 ''' <param name="enCharge">充值记录,只包含操作员就行</param>
144 ''' <returns>该操作员未结账的充值总额</returns>
145 ''' <remarks></remarks>
146 Private Function chargeSumFindByOper(ByVal enCharge As enCharge) As DataTable
147 sqlHelper = New SQLHelper()
148 Dim dt As New DataTable '用来接收查询的结果
149 '给sql语句赋值
150 strSql = "select sum(Charge) from Charge where OperatorName=@Oper and OrderState='未结账'"
151 cmd = New SqlCommand(strSql, sqlHelper.conGet())
152 sqlHelper.SqlParameterAdd(cmd, "@Oper", SqlDbType.VarChar, enCharge.OperatorName)
153 dt = sqlHelper.ExecuteQuery(strSql, cmd)
154 Return dt
155 End Function
156 End Class