重构个人版机房收费系统——模板模式

时间:2022-12-20 15:20:36

1、背景


在开始本篇博文之前,我们先来看一下下面截取的几幅图片:

1、组合查询

       重构个人版机房收费系统——模板模式    


  2、根据卡号查询记录并导出到Excel表格

         重构个人版机房收费系统——模板模式


3、按照指定的时间范围查询记录

重构个人版机房收费系统——模板模式


        是的,放眼望去,如果除去要实现的功能只考虑这三组图片的外观,那么每组的两张图片简直就是一个用模子刻出来的嘛~~~~~这就涉及到博文的主题了——模板模式。

2、模板模式

1、什么是模板模式

      模板模式,提供了一个代码复用平台,当不变和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现,形成了代码冗余,而且不易于维护,而模板模式就是把这些不变行为搬移到单一的地方(即超类),然后通过多态实现代码的复用。


2、机房收费实例讲解

      有了模板模式,最直观的一点好处就是再也不用花费很多时间在设置窗体界面上了(特别是组合查询),只要做个模子,直接“刻”就行啦~~。接下来就通过一个实例:学生上机信息查询,来增加一下理解。


1,首先正常添加windows窗体,并设置好模板窗体的界面。定义相关抽象类

重构个人版机房收费系统——模板模式


抽象模板:

Imports Entity
Imports chargeManageBLL
Public Class frmGroupQuery

Protected enQueryGroup As New En_QueryGroup
''' <summary>
''' 窗体加载
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub frmGroupQuery_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'将参数传递给实体,赋初值
enQueryGroup.fileds1 = "1"
enQueryGroup.fileds2 = "1"
enQueryGroup.fileds3 = "1"

combOperA.Items.Add("=")
combOperA.Items.Add("<")
combOperA.Items.Add(">")
combOperA.Items.Add("<>")

combOperB.Items.Add("=")
combOperB.Items.Add("<")
combOperB.Items.Add(">")
combOperB.Items.Add("<>")

combOperC.Items.Add("=")
combOperC.Items.Add("<")
combOperC.Items.Add(">")
combOperC.Items.Add("<>")

combRelationA.Items.Add("与")
combRelationA.Items.Add("或")

combRelationB.Items.Add("与")
combRelationB.Items.Add("或")

'窗体加载,后两组控件默认为不可用
combFiledB.Enabled = False
combFiledC.Enabled = False
combOperB.Enabled = False
combOperC.Enabled = False

combRelationB.Enabled = False
txtContentB.Enabled = False
txtContentC.Enabled = False

End Sub
''' <summary>
''' 点击查询按钮,触发的事件
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnSelect_Click(sender As Object, e As EventArgs) Handles btnSelect.Click

'不是组合查询
'限制输入条件不能为空
If combRelationA.Text = "" Then
If combFiledA.Text = "" Or combOperA.Text = "" Or txtContentA.Text = "" Then
MsgBox("查询条件不能为空,请填写完全!", vbOKOnly, "提示信息")
Exit Sub
End If
End If

'根据第一个组合查询条件文本内容是否为空判断下一组控件的内容是否为空
If combRelationA.Text <> "" Then
If combFiledA.Text = "" Or combOperA.Text = "" Or txtContentA.Text = "" Or combFiledB.Text = "" Or combOperB.Text = "" Or txtContentB.Text = "" Then
MsgBox("查询条件不能为空,请填写完全!", vbOKOnly, "提示信息")
Exit Sub
End If
End If

'根据第二个组合查询条件文本内容是否为空判断下一组控件的内容是否为空
If combRelationB.Text <> "" Then
If combFiledA.Text = "" Or combOperA.Text = "" Or txtContentA.Text = "" Or combFiledB.Text = "" Or combOperB.Text = "" Or txtContentB.Text = "" Or combFiledC.Text = "" Or combOperC.Text = "" Then
MsgBox("查询条件不能为空,请填写完全!", vbOKOnly, "提示信息")
Exit Sub
End If
End If

Dim dt As New DataTable
Dim queryOperator As New F_GroupQuery '实例化外观

'将参数传递给实体封装
enQueryGroup.fileds1 = GetEnglish(combFiledA.Text)
enQueryGroup.fileds2 = GetEnglish(combFiledB.Text)
enQueryGroup.fileds3 = GetEnglish(combFiledB.Text)
enQueryGroup.operator1 = combOperA.Text
enQueryGroup.operator2 = combOperB.Text
enQueryGroup.operator3 = combOperC.Text
enQueryGroup.content1 = txtContentA.Text.Trim
enQueryGroup.content2 = txtContentB.Text.Trim
enQueryGroup.content3 = txtContentC.Text.Trim
enQueryGroup.relation1 = combRelationA.Text.Trim
enQueryGroup.relation2 = combRelationB.Text.Trim

Dim strsql As String
strsql = SelectData()

End Sub


''' <summary>
''' 模板方法:定义虚函数GetEnglish,查询字段转化为数据库字段
''' </summary>
''' <param name="combField"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Function GetEnglish(combField As String) As String
Return ""
End Function
''' <summary>
''' 定义虚函数GetTable,获取不同数据库的表名
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Function GetTable() As String
Return ""
End Function
''' <summary>
''' 执行查询的关键函数
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Protected Overridable Function SelectData()
Return ""
End Function
''' <summary>
''' 拼接字符串
''' </summary>
''' <param name="frm"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function Query(frm As frmGroupQuery) As String
Dim sql As String = "" & frm.GetEnglish(frm.combFiledA.Text) & frm.combOperA.Text & "'" & frm.txtContentA.Text & "'"
'非组合查询
If frm.combRelationA.Text = "" Then
sql = sql
Else
'第一个条件不为空,第二个条件为空
If frm.combRelationB.Text = "" Then
sql = sql & frm.GetEnglish(frm.combRelationA.Text) & " " & frm.GetEnglish(frm.combFiledB.Text) & frm.combOperB.Text & "'" & frm.txtContentB.Text & "'"
Else
'两个条件均不为空
sql = sql & frm.GetEnglish(frm.combRelationA.Text) & " " & frm.GetEnglish(frm.combFiledB.Text) & frm.combOperB.Text & "'" & frm.txtContentB.Text & "'" & "" & frm.GetEnglish(frm.combRelationB.Text) & " " & frm.GetEnglish(frm.combFiledC.Text) & frm.combOperB.Text & "'" & frm.txtContentC.Text & "'"
End If
End If
Return sql
End Function

''' <summary>
''' 第一个组合关系不为空
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub combRelationA_SelectedIndexChanged(sender As Object, e As EventArgs) Handles combRelationA.SelectedIndexChanged

combFiledB.Enabled = True
combOperB.Enabled = True
txtContentB.Enabled = True
combRelationB.Enabled = True

combFiledC.Enabled = False
combOperC.Enabled = False
txtContentC.Enabled = False
End Sub
''' <summary>
''' 第二个组合关系不为空
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub combRelationB_SelectedIndexChanged(sender As Object, e As EventArgs) Handles combRelationB.SelectedIndexChanged

combFiledC.Enabled = True
combOperC.Enabled = True
txtContentC.Enabled = True

End Sub
End Class

      设置完模板窗体后,接下来就是“刻”子窗体了。添加新项——继承的窗体,设置窗体名称,点击“添加”,在弹出的继承器窗口中选择要刚刚设置的模板窗体就Ok啦。


重构个人版机房收费系统——模板模式


通过模板刻制出来的窗体,需要的排版、控件等等都与模板窗体完全一致,不错吧~~~


重构个人版机房收费系统——模板模式


子类,实现父类所定义的一个或多个抽象方法。

<span style="font-size:14px;">Imports Entity
Imports chargeManageBLL
Public Class frmOnLine

Private Sub frmOnLine_Load(sender As Object, e As EventArgs) Handles MyBase.Load
combFiledA.Items.Add("卡号")
combFiledA.Items.Add("姓名")
combFiledA.Items.Add("上机日期")
combFiledA.Items.Add("上机时间")
combFiledA.Items.Add("下机日期")
combFiledA.Items.Add("下机时间")
combFiledA.Items.Add("消费金额")
combFiledA.Items.Add("余额")

combFiledB.Items.Add("卡号")
combFiledB.Items.Add("姓名")
combFiledB.Items.Add("上机日期")
combFiledB.Items.Add("上机时间")
combFiledB.Items.Add("下机日期")
combFiledB.Items.Add("下机时间")
combFiledB.Items.Add("消费金额")
combFiledB.Items.Add("余额")

combFiledC.Items.Add("卡号")
combFiledC.Items.Add("姓名")
combFiledC.Items.Add("上机日期")
combFiledC.Items.Add("上机时间")
combFiledC.Items.Add("下机日期")
combFiledC.Items.Add("下机时间")
combFiledC.Items.Add("消费金额")
combFiledC.Items.Add("余额")
End Sub

Public Overrides Function GetEnglish(combField As String) As String
Select Case combField
Case "卡号"
GetEnglish = "cardID"
Case "姓名"
GetEnglish = "studentName"
Case "上机日期"
GetEnglish = "onDate"
Case "上机时间"
GetEnglish = "onTime"
Case "下机日期"
GetEnglish = "offDate"
Case "下机时间"
GetEnglish = "offTime"
Case "消费金额"
GetEnglish = "consumeMoney"
Case "余额"
GetEnglish = "cash"
Case "与"
GetEnglish = "and"
Case "或"
GetEnglish = "or"
Case Else
GetEnglish = ""
End Select
End Function
Public Overrides Function GetTable() As String
Return "OnLine_table"
End Function

Protected Overrides Function SelectData() As Object
Dim dt As New DataTable
Dim facaonlineinfo As New F_GroupQuery
Dim frmgroupquery As New frmGroupQuery
Dim sql As String
Try
sql = frmgroupquery.Query(Me)
dt = facaonlineinfo.QueryOnLineInfo(sql)
If dt.Rows.Count = 0 Then
dt.Clear()
DataGridView1.DataSource = Nothing
DataGridView1.Refresh()
Else
DataGridView1.DataSource = dt
DataGridView1.Columns(0).Visible = False
DataGridView1.Columns(0).HeaderText = "卡号"
DataGridView1.Columns(1).HeaderText = "姓名"
DataGridView1.Columns(2).HeaderText = "上机日期"
DataGridView1.Columns(3).HeaderText = "上机时间"
DataGridView1.Columns(4).HeaderText = "下机日期"
DataGridView1.Columns(5).HeaderText = "下机时间"
DataGridView1.Columns(6).HeaderText = "消费金额"
DataGridView1.Columns(7).HeaderText = "余额"

End If
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示信息")
End Try
End Function
End Class</span>

        跟以往的类抽象不同的是,模板方法是一种反向的控制结构,这种结构有时被称为“好莱坞法则”,即“别找我们,我们找你”。这指的是一个父类调用一个子类的操作,而不是相反,在程序执行时进行跟踪即可验证。


       导出到Excel表格跟VB6.0有些不同,每个窗体都一样,所以该功能提炼到模板窗体中实现即可。

首先,设置datagridview控件的[AllowUserToAddRows]属性为False,否则会出现错误:未将对象引用到实例。

<span style="font-size:14px;">''' <summary>
''' 导出到Excel
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnExportExcel_Click(sender As Object, e As EventArgs) Handles btnExportExcel.Click
Dim MyExcel As New Excel.Application '添加工作簿
MyExcel.Application.Workbooks.Add() '添加表
MyExcel.Visible = True '打开表

'获取datagridview的标题行赋给Excel
'Excel标题行第一列标识为1,datagridview则为0,所以cols-1
Dim Cols As Integer
For Cols = 1 To DataGridView1.Columns.Count
MyExcel.Cells(1, Cols) = DataGridView1.Columns(Cols - 1).HeaderText
Next

'往Excel表中添加数据
Dim i As Integer
For i = 0 To DataGridView1.RowCount - 1
Dim j As Integer
For j = 0 To DataGridView1.ColumnCount - 1
If Me.DataGridView1(j, i).Value Is System.DBNull.Value Then
MyExcel.Cells(i + 2, j + 1) = ""
Else
MyExcel.Cells(i + 2, j + 1) = DataGridView1(j, i).Value.ToString()

End If
Next
Next</span>

        通过重构个人版机房收费,对部分设计模式有了更多的实际应用的机会,经过实战练习,在原来学习的基础上有了更深的理解。更多的知识还需要不断地去学习和积累。