在敲机房收费过程中,我们会发现很多窗体除了一些细微的差别外,基本是一模一样的,功能的实现也是大同小异。在第一次机房收费的时候,我们都是“好学生”,尽管代码重复率极高,还是按部就班的一个个的实现。但在学习了设计模式,机房重构的现在,再傻傻的重复代码,就不是明智之举了。
整个收费系统中,总计有四个组合查询的功能(界面如图),为了提高代码复用率,提高效率,就引入了模板方法实现。
模板方法模式,定义了一个操作中的算法的骨架,把一些步骤延迟到子类当中。它使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。它提供了一个代码复用的平台,消除代码冗余带来的弊端,使系统更易于维护。
那下面就通过机房收费上机学生的组合查询来体会一下模板方法的魅力。
1、建立模板父窗体
添加Windows窗体,设计模板界面(如上图),并在模板窗体里写入抽象出来的类和方法的代码。
Public Class frmGroupQuery
Protected enGroupQuery As New GroupQueryEntity
Protected Sub frmGroupQuery_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'将参数传递给实体,赋初值
'因为不同窗体字段不同,所以赋“”,子窗体重写它
enGroupQuery.cboName1 = ""
enGroupQuery.cboName2 = ""
enGroupQuery.cboName3 = ""
'操作符
cboMark1.Items.Add(">")
cboMark1.Items.Add("<")
cboMark1.Items.Add("=")
cboMark1.Items.Add("<>")
cboMark2.Items.Add(">")
cboMark2.Items.Add("<")
cboMark2.Items.Add("=")
cboMark2.Items.Add("<>")
cboMark3.Items.Add(">")
cboMark3.Items.Add("<")
cboMark3.Items.Add("=")
cboMark3.Items.Add("<>")
'关系
cboRelation1.Items.Add("与")
cboRelation1.Items.Add("或")
cboRelation2.Items.Add("与")
cboRelation2.Items.Add("或")
'窗体加载后,后两组控件默认不可用
cboName2.Enabled = False
cboName3.Enabled = False
cboMark2.Enabled = False
cboMark3.Enabled = False
cboRelation2.Enabled = False
txtContent2.Enabled = False
txtContent3.Enabled = False
'设置选中单元格就选中行
dgvRecord.SelectionMode = DataGridViewSelectionMode.FullRowSelect
Dim i As Integer
For i = 0 To dgvRecord.Columns.Count - 1
dgvRecord.Columns(i).Width = DataGridViewAutoSizeColumnsMode.AllCells
Next
End Sub
Protected Sub btnQuery_Click(sender As Object, e As EventArgs) Handles btnQuery.Click
'判断组合框不为空
If cboRelation1.Text = "" Then
If cboName1.Text = "" Or cboMark1.Text = "" Or txtContent1.Text = "" Then
MsgBox("第一行查询条件不能为空,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")
Exit Sub
End If
End If
If cboRelation1.Text <> "" Then
If cboName1.Text = "" Or cboMark1.Text = "" Or txtContent1.Text = "" Or
cboName2.Text = "" Or cboMark2.Text = "" Or txtContent2.Text = "" Then
MsgBox("第二行查询条件不能为空,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")
Exit Sub
End If
Else
If cboRelation2.Text <> "" Then
If cboName1.Text = "" Or cboMark1.Text = "" Or txtContent1.Text = "" Or
cboName2.Text = "" Or cboMark2.Text = "" Or txtContent2.Text = "" Or
cboName3.Text = "" Or cboMark3.Text = "" Or txtContent3.Text = "" Then
MsgBox("第三行查询条件不能为空,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")
Exit Sub
End If
End If
End If
'将参数传给实体
enGroupQuery.dbName = GetdbName()
enGroupQuery.cboName1 = ToEnglish(cboName1.Text)
enGroupQuery.cboName2 = ToEnglish(cboName2.Text)
enGroupQuery.cboName3 = ToEnglish(cboName3.Text)
enGroupQuery.cboMark1 = cboMark1.Text.Trim
enGroupQuery.cboMark2 = cboMark2.Text.Trim
enGroupQuery.cboMark3 = cboMark3.Text.Trim
enGroupQuery.txtContent1 = txtContent1.Text.Trim
enGroupQuery.txtContent2 = txtContent2.Text.Trim
enGroupQuery.txtContent3 = txtContent3.Text.Trim
''前者还是后者
enGroupQuery.cboRelation1 = ToEnglish(cboRelation1.Text)
enGroupQuery.cboRelation2 = ToEnglish(cboRelation2.Text)
Dim dt As New DataTable
Dim GroupQueryBLL As New BLL.GroupQueryBLL
Call Todgv()
End Sub
''' <summary>
''' 模板方法,定义虚函数ToEnglish,查询字段转化为数据库字段
''' </summary>
''' <param name="cboName"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Function ToEnglish(cboName As String) As String
Return ""
End Function
''' <summary>
''' 获得数据库表名
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Protected Overridable Function GetdbName() As String
Return ""
End Function
''' <summary>
''' 把表显示到datagridview中
''' </summary>
''' <remarks></remarks>
Protected Overridable Sub Todgv()
End Sub
''' <summary>
''' 拼接字符串
''' </summary>
''' <param name="frm"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function Query(frm As frmGroupQuery) As String
Dim cmdText As String = "" & frm.ToEnglish(frm.cboName1.Text) & frm.cboMark1.Text & "'" & frm.txtContent1.Text & "'"
'非组合查询
If frm.cboRelation1.Text = "" Then
cmdText = cmdText
Else
'关系1为空,关系2不为空
If frm.cboRelation1.Text <> "" Then
cmdText = cmdText & frm.ToEnglish(frm.cboRelation1.Text) & "" &
frm.ToEnglish(frm.cboName2.Text) & frm.cboMark2.Text & "'" & frm.txtContent2.Text & "'"
Else
'关系1、2 都不为空
cmdText = cmdText & frm.ToEnglish(frm.cboRelation1.Text) & "" &
frm.ToEnglish(frm.cboName2.Text) & frm.cboMark2.Text & "'" & frm.txtContent2.Text & "'" & "" &
frm.ToEnglish(frm.cboRelation2.Text) & "" &
frm.ToEnglish(frm.cboName3.Text) & frm.cboMark3.Text & "'" & frm.txtContent2.Text & "'"
End If
End If
Return cmdText
End Function
''' <summary>
''' 第一个组合关系不为空
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub cboRelation1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboRelation1.SelectedIndexChanged
If cboRelation1.Text = "" Then
cboName2.Enabled = False
cboName3.Enabled = False
cboMark2.Enabled = False
cboMark3.Enabled = False
cboRelation2.Enabled = False
txtContent2.Enabled = False
txtContent3.Enabled = False
Else
cboName2.Enabled = True
cboMark2.Enabled = True
cboRelation2.Enabled = True
txtContent2.Enabled = True
End If
End Sub
''' <summary>
''' 第二个组合关系不为空
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub cboRelation2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboRelation2.SelectedIndexChanged
If cboRelation2.Text = "" Then
cboName3.Enabled = False
cboMark3.Enabled = False
txtContent3.Enabled = False
Else
cboName3.Enabled = True
cboMark3.Enabled = True
txtContent3.Enabled = True
End If
End Sub
Protected Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
Me.Close()
End Sub
End Class
2、建立子窗体
如下图建立子窗体,选择继承创建的父窗体模板,然后就可以得到一模一样的子窗体了。通过在子窗体里重写一些方法和类,以实现不同的功能就可以了。
实现上机学生查询的代码如下:
Public Class frmOnlineQuery
''' <summary>
''' 加载combo的item
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub frmOnlineQuery_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cboName1.Items.Add("卡号")
cboName1.Items.Add("姓名")
cboName1.Items.Add("上机日期")
cboName1.Items.Add("上机时间")
cboName1.Items.Add("机器名")
cboName2.Items.Add("卡号")
cboName2.Items.Add("姓名")
cboName2.Items.Add("上机日期")
cboName2.Items.Add("上机时间")
cboName2.Items.Add("机器名")
cboName3.Items.Add("卡号")
cboName3.Items.Add("姓名")
cboName3.Items.Add("上机日期")
cboName3.Items.Add("上机时间")
cboName3.Items.Add("机器名")
End Sub
''' <summary>
''' 把加载的汉字转换成数据库的字段
''' </summary>
''' <param name="cboName"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overrides Function ToEnglish(cboName As String) As String
Select Case cboName
Case "卡号"
ToEnglish = "CardID"
Case "姓名"
ToEnglish = "StuID"
Case "上机日期"
ToEnglish = "StuLoginDate"
Case "上机时间"
ToEnglish = "StuLoginTime"
Case "机器名"
ToEnglish = "Computer"
Case "与"
ToEnglish = "and"
Case "或"
ToEnglish = "or"
Case Else
ToEnglish = ""
End Select
End Function
''' <summary>
''' 传数据库表名
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Protected Overrides Function GetdbName() As String
Return "T_OnLineInfo"
End Function
''' <summary>
''' 把数据显示到datagridview中
''' </summary>
''' <remarks></remarks>
Protected Overrides Sub Todgv()
Dim dt As New DataTable
Dim frmGropQuery As New frmGroupQuery
Dim GroupQueryBLL As New BLL.GroupQueryBLL
Try
dt = GroupQueryBLL.GroupQuery(enGroupQuery)
If dt.Rows.Count = 0 Then
dt.Clear()
dgvRecord.DataSource = Nothing
dgvRecord.Refresh()
Else
dgvRecord.DataSource = dt
dgvRecord.Columns(0).Visible = False
dgvRecord.Columns(1).HeaderText = "卡号"
dgvRecord.Columns(2).HeaderText = "姓名"
dgvRecord.Columns(3).Visible = False
dgvRecord.Columns(4).HeaderText = "上机日期"
dgvRecord.Columns(5).HeaderText = "上机时间"
dgvRecord.Columns(6).Visible = False
dgvRecord.Columns(7).Visible = False
dgvRecord.Columns(8).Visible = False
dgvRecord.Columns(9).Visible = False
dgvRecord.Columns(10).Visible = False
dgvRecord.Columns(11).Visible = False
dgvRecord.Columns(12).HeaderText = "机器名"
End If
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示")
End Try
End Sub
End Class