【机房重构】组合查询——模板方法

时间:2022-09-16 11:17:57

前言

    我们在学习第一次vb版机房收费系统的时候,面对组合查询,面对三个差异很小的窗体,我们的解决办法就是ctrl+c,ctrl+v。若干个月之后,我们学习了设计模式相关知识,再去解决组合查询时,才发现设计模式如此具有趣味性,它可以让我们的学习变得更加高效。


主要内容

1.模板方法模式

    大话设计模式中的实例是"考题抄错会做也白搭",其中大概意思是,如果很多内容类似,我们只需设置一个模板,让所有类似的内容使用这个模板,这时只需修改很少的部分。

    官话“模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。”

    这里需要注意的是,模板方法很重要的思想是子类继承父类,通过重写实现功能。

2.实现

(1)建立父窗体,添加代码

 【机房重构】组合查询——模板方法


UI层

Public Class frmGroupCheck
'定义一个变量,用于接收文本框或者时间控件传递的内容
Public txt1 As String = ""
Public txt2 As String = ""
Public txt3 As String = ""
'定义实体
Protected groupcheck As New Entity.GroupCheckEntity

'窗体加载
Private Sub frmGroupCheck_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'操作符
cmbOperator1.Items.Add(">")
cmbOperator1.Items.Add("<")
cmbOperator1.Items.Add("=")
cmbOperator1.Items.Add("<>")

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

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

'关系
cmbRelations1.Items.Add("与")
cmbRelations1.Items.Add("或")

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

'DateTimePicker控件的可见性
dtp1.Visible = False
dtp2.Visible = False
dtp3.Visible = False

'在加载的时候,只有第一行控件可用,后两行控件不可用
cmbField2.Enabled = False
cmbField3.Enabled = False
cmbOperator2.Enabled = False
cmbOperator3.Enabled = False
cmbRelations2.Enabled = False
txtContent2.Enabled = False
txtContent3.Enabled = False

'当选中dgvGroupCheck控件就选中行
dgvGroupCheck.SelectionMode = DataGridViewSelectionMode.FullRowSelect
Dim i As Integer
For i = 0 To dgvGroupCheck.Columns.Count - 1
dgvGroupCheck.Columns(i).Width = DataGridViewAutoSizeColumnMode.AllCells
Next
End Sub

'清空
Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
cmbField1.Text = ""
cmbField2.Text = ""
cmbField3.Text = ""

cmbOperator1.Text = ""
cmbOperator2.Text = ""
cmbOperator3.Text = ""

txtContent1.Text = ""
txtContent2.Text = ""
txtContent3.Text = ""

cmbRelations1.Text = ""
cmbRelations2.Text = ""

dgvGroupCheck.DataSource = ""
End Sub

'退出
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
Me.Close()
End Sub

'查询
Private Sub btnInquiry_Click(sender As Object, e As EventArgs) Handles btnInquiry.Click
'判断组合框不为空
If cmbRelations1.Text = "" Then '如果第一个组合关系框为空
If cmbField1.Text = "" Or cmbOperator1.Text = "" Or txt1 = "" Then
MsgBox("第一行查询条件不能为空!", , "提示")
Exit Sub
End If
End If

If cmbRelations1.Text <> "" Then '如果选择了第一个组合关系
If cmbField2.Text = "" Or cmbOperator2.Text = "" Or txt2 = "" Then
MsgBox("第二行查询条件不能为空!", , "提示")
Exit Sub
End If
Else
If cmbRelations2.Text <> "" Then '选择了第二个组合关系
If cmbField1.Text = "" Or cmbOperator1.Text = "" Or txt1 = "" Or
cmbField2.Text = "" Or cmbField2.Text = "" Or txt2 = "" Or
cmbField3.Text = "" Or cmbField3.Text = "" Or txt3 = "" Then
MsgBox("第三行查询条件不能为空", , "提示")
Exit Sub
End If
End If
End If

'给实体赋值
groupcheck.tableName = GetTable()

groupcheck.cmbField1 = GetDBName(cmbField1.Text.Trim())
groupcheck.cmbField2 = GetDBName(cmbField2.Text.Trim())
groupcheck.cmbField3 = GetDBName(cmbField3.Text.Trim())

groupcheck.cmbOperator1 = cmbOperator1.Text.Trim()
groupcheck.cmbOperator2 = cmbOperator2.Text.Trim()
groupcheck.cmbOperator3 = cmbOperator3.Text.Trim()

groupcheck.txtContent1 = txt1
groupcheck.txtContent2 = txt2
groupcheck.txtContent3 = txt3

groupcheck.cmbRelations1 = GetDBName(cmbRelations1.Text.Trim())
groupcheck.cmbRelations2 = GetDBName(cmbRelations2.Text.Trim())

'给外观层方法传递参数
Dim table As New DataTable
Dim Fgroupcheck As New Facade.GroupCheckFacade
table = Fgroupcheck.selectGroupCheck(groupcheck)
If (table.Rows.Count = 0) Then
MsgBox("没有符合条件的记录,请重新设置查询条件", , "提示")
dgvGroupCheck.DataSource = Nothing
Else
Call Todatagridview()
End If
End Sub

'当第一个组合关系框的内容发生改变时,设置第二行查询框的状态(是否可用)
Private Sub cmbRelations1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbRelations1.SelectedIndexChanged
cmbField2.Enabled = True
cmbOperator2.Enabled = True
txtContent2.Enabled = True
cmbRelations2.Enabled = True
End Sub

'当第二个组合关系框的内容发生改变时,设置第三行查询框的状态(是否可用)
Private Sub cmbRelations2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbRelations2.SelectedIndexChanged
cmbField3.Enabled = True
cmbOperator3.Enabled = True
txtContent3.Enabled = True
End Sub

'定义虚函数GetDBName,获取不同数据库的字段名
Public Overridable Function GetDBName(ByVal control As String) As String
Return ""
End Function

'定义虚函数GetTable,获取不同数据库的表名
Protected Overridable Function GetTable() As String
Return ""
End Function
'把表显示到dgvGroupCheck中
Protected Overridable Sub Todatagridview()
dgvGroupCheck.AutoSizeColumnsMode = DataGridViewAutoSizeColumnMode.AllCells
End Sub

'针对查询与日期、时间有关的问题
Private Sub cmbField1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbField1.SelectedIndexChanged
If cmbField1.Text = "上机日期" Or cmbField1.Text = "下机日期" Or cmbField1.Text = "上机时间" Or cmbField1.Text = "下机时间" Then
dtp1.Format = DateTimePickerFormat.Custom
dtp1.Visible = True
txtContent1.Visible = False
If cmbField1.Text = "上机日期" Or cmbField1.Text = "下机日期" Then
txt1 = Format(dtp1.Value, "yyyy-MM-dd")
ElseIf cmbField1.Text = "上机时间" Or cmbField1.Text = "下机时间" Then
dtp1.Format = DateTimePickerFormat.Time
txt1 = Format(dtp1.Value, "hh:mm:ss")
End If
Else
txt1 = txtContent1.Text
End If
End Sub

'针对查询与日期、时间有关的问题
Private Sub cmbField2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbField2.SelectedIndexChanged
If cmbField2.Text = "上机日期" Or cmbField2.Text = "下机日期" Or cmbField2.Text = "上机时间" Or cmbField2.Text = "下机时间" Then
dtp2.Format = DateTimePickerFormat.Custom
dtp2.Visible = True
txtContent2.Visible = False
If cmbField2.Text = "上机日期" Or cmbField2.Text = "下机日期" Then
txt2 = Format(dtp2.Value, "yyyy-MM-dd")
ElseIf cmbField2.Text = "上机时间" Or cmbField2.Text = "下机时间" Then
dtp2.Format = DateTimePickerFormat.Time
txt2 = Format(dtp2.Value, "hh:mm:ss")
End If
Else
txt2 = txtContent2.Text
End If
End Sub

'针对查询与日期、时间有关的问题
Private Sub cmbField3_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbField3.SelectedIndexChanged
If cmbField3.Text = "上机日期" Or cmbField3.Text = "下机日期" Or cmbField3.Text = "上机时间" Or cmbField3.Text = "下机时间" Then
dtp3.Format = DateTimePickerFormat.Custom
dtp3.Visible = True
txtContent3.Visible = False
If cmbField3.Text = "上机日期" Or cmbField3.Text = "下机日期" Then
txt3 = Format(dtp3.Value, "yyyy-MM-dd")
ElseIf cmbField3.Text = "上机时间" Or cmbField3.Text = "下机时间" Then
dtp3.Format = DateTimePickerFormat.Time
txt3 = Format(dtp3.Value, "hh:mm:ss")
End If
Else
txt3 = txtContent3.Text
End If
End Sub
End Class

Facade层

Public Function selectGroupCheck(GroupCheck As Entity.GroupCheckEntity) As DataTable
Dim GroupCheckBLL As New BLL.GroupCheckBLL
Dim table As New DataTable
table = GroupCheckBLL.selectGroupCheck(GroupCheck)
Return table
End Function
B层

Public Function selectGroupCheck(ByVal GroupCheck As Entity.GroupCheckEntity) As DataTable
Dim Factory As New Factory.Fac
Dim IGroupCheck As IDAL.IGroupCheckDAL = Factory.GroupCheck
Dim table As New DataTable
table = IGroupCheck.selectGroupCheck(GroupCheck)
If table.Rows.Count = 0 Then
Return Nothing
Else
Return table
End If
End Function
D层

Imports System.Data.SqlClient
Public Class GroupCheckDAL : Implements IDAL.IGroupCheckDAL
Private SqlHelper As SQLHelper.sqlhelper = New SQLHelper.sqlhelper()
Public Function selectGroupCheck(ByVal GroupCheck As Entity.GroupCheckEntity) As DataTable Implements IDAL.IGroupCheckDAL.selectGroupCheck
Dim sql As String = "PROC_GroupCheck" '调用存储过程
Dim table As DataTable
Dim sqlparams As SqlParameter() = {New SqlParameter("@cmbField1", GroupCheck.cmbField1),
New SqlParameter("@cmbField2", GroupCheck.cmbField2),
New SqlParameter("@cmbField3", GroupCheck.cmbField3),
New SqlParameter("@cmbOperator1", GroupCheck.cmbOperator1),
New SqlParameter("@cmbOperator2", GroupCheck.cmbOperator2),
New SqlParameter("@cmbOperator3", GroupCheck.cmbOperator3),
New SqlParameter("@txtContent1", GroupCheck.txtContent1),
New SqlParameter("@txtContent2", GroupCheck.txtContent2),
New SqlParameter("@txtContent3", GroupCheck.txtContent3),
New SqlParameter("@cmbRelations1", GroupCheck.cmbRelations1),
New SqlParameter("@cmbRelations2", GroupCheck.cmbRelations2),
New SqlParameter("@tableName", GroupCheck.tableName)} '设置参数
table = SqlHelper.GetDataTable(sql, CommandType.StoredProcedure, sqlparams)
Return table
End Function
Entity层

    Private _cmbField1 As String
Private _cmbField2 As String
Private _cmbField3 As String
Private _cmbOperator1 As String
Private _cmbOperator2 As String
Private _cmbOperator3 As String
Private _txtContent1 As String
Private _txtContent2 As String
Private _txtContent3 As String
Private _cmbRelations1 As String
Private _cmbRelations2 As String
Private _tableName As String

存储过程

USE [charge]
GO
/****** Object: StoredProcedure [dbo].[PROC_GroupCheck] Script Date: 04/28/2017 17:27:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: 李佳丽
-- Create date: 2017-4-19
-- Description: 用于组合查询时和数据库交互
-- =============================================
ALTER PROCEDURE [dbo].[PROC_GroupCheck]
@cmbField1 varchar(20),
@cmbOperator1 varchar(20),
@txtContent1 varchar(20),
@cmbField2 varchar(20),
@cmbOperator2 varchar(20),
@txtContent2 varchar(20),
@cmbField3 varchar(20),
@cmbOperator3 varchar(20),
@txtContent3 varchar(20),
@cmbRelations1 varchar(20),
@cmbRelations2 varchar(20),
@tableName varchar(20)
AS
declare @TempSql varchar(500)
BEGIN
SET @TempSql='SELECT * FROM '+@tableName +' WHERE ' +@cmbField1 +@cmbOperator1+char(39) + @txtContent1 + char(39)
if (@cmbRelations1!='')
BEGIN
SET @TempSql=@TempSql+@cmbRelations1+CHAR(32)+@cmbField2 +@cmbOperator2+CHAR(39)+@txtContent2+CHAR(39)
if (@cmbRelations2!='')
BEGIN
SET @TempSql=@TempSql+@cmbRelations2+CHAR(32)+@cmbField3+@cmbOperator3+CHAR(39)+@txtContent3+CHAR(39)
END
END
EXECUTE(@TempSql)
END

(2)建立子窗体,添加代码

    右击命名空间(例如UI)-添加-类-已安装-常用项-Windows Forms-继承的窗体-选择继承的父类窗体

【机房重构】组合查询——模板方法

  UI层代码

Public Class frmOpWorkRecord
'单例模式
Private Shared frmOpWorkRecord As New frmOpWorkRecord
Private Sub New()
' 此调用是设计器所必需的。
InitializeComponent()
' 在 InitializeComponent() 调用之后添加任何初始化。
End Sub
Public Shared Function Getinstance() As frmOpWorkRecord
If frmOpWorkRecord Is Nothing OrElse frmOpWorkRecord.IsDisposed Then
frmOpWorkRecord = New frmOpWorkRecord
frmOpWorkRecord.MdiParent = frmMain
End If
Return frmOpWorkRecord
End Function

'重写转换成数据库字段的方法
Public Overrides Function GetDBName(control As String) As String
Select Case (control)
Case "教师"
Return "UserID"
Case "级别"
Return "Level"
Case "上机日期"
Return "LoginDate"
Case "上机时间"
Return "LoginTime"
Case "下机日期"
Return "LogoutDate"
Case "下机时间"
Return "LogoutTime"
Case "机器名"
Return "Computer"
Case "状态"
Return "Online"
Case "与"
Return "and"
Case "或"
Return "or"
Case Else
Return ""
End Select
End Function

'重获表名的方法
Protected Overrides Function GetTable() As String
groupcheck.tableName = "WorkLog_Info"
Return groupcheck.tableName
End Function

'窗体加载
Private Sub frmOpWorkRecord_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cmbField1.Items.Add("教师")
cmbField1.Items.Add("上机日期")
cmbField1.Items.Add("上机时间")
cmbField1.Items.Add("下机日期")
cmbField1.Items.Add("下机时间")
cmbField1.Items.Add("机器名")

cmbField2.Items.Add("教师")
cmbField2.Items.Add("上机日期")
cmbField2.Items.Add("上机时间")
cmbField2.Items.Add("下机日期")
cmbField2.Items.Add("下机时间")
cmbField2.Items.Add("机器名")

cmbField3.Items.Add("教师")
cmbField3.Items.Add("上机日期")
cmbField3.Items.Add("上机时间")
cmbField3.Items.Add("下机日期")
cmbField3.Items.Add("下机时间")
cmbField3.Items.Add("机器名")
End Sub

'重写方法
Protected Overrides Sub Todatagridview()
Dim table As New DataTable
Dim frmGroupCheck As New frmGroupCheck
Dim FacadeGroupCheck As New Facade.GroupCheckFacade
Try
table = FacadeGroupCheck.selectGroupCheck(groupcheck)
If table.Rows.Count = 0 Then
table.Clear()
dgvGroupCheck.DataSource = Nothing
dgvGroupCheck.Refresh()
MsgBox("记录为空")
Else
dgvGroupCheck.DataSource = table

dgvGroupCheck.Columns(0).HeaderText = "用户名"
dgvGroupCheck.Columns(1).HeaderText = "级别"
dgvGroupCheck.Columns(2).HeaderText = "上机日期"
dgvGroupCheck.Columns(3).HeaderText = "上机时间"
dgvGroupCheck.Columns(4).HeaderText = "下机日期"
dgvGroupCheck.Columns(5).HeaderText = "下机时间"
dgvGroupCheck.Columns(5).HeaderText = "机器名"
dgvGroupCheck.Columns(5).HeaderText = "状态"
End If
Catch ex As Exception
MsgBox(ex.Message, vbOKOnly, "提示")
End Try
End Sub
End Class

3.遇到的问题

(1)@cmbRelations1!='' vs  @cmbRelations1 is not null(存储过程中)

    这个问题的关注点就在于“”与null的区别,可以查看一下数据库默认添加的值是空还是null。

【机房重构】组合查询——模板方法

(2)日期怎么传参?

    当我们查询的内容是字符串类型时,直接将text值作为参数传递就可以。当我们查询的内容为日期(时间)时,需要将DateTimePicker的值作为参数传递,由于两个控件只可能传递一个参数,所以我们可以设一个变量去接收参数。

(3)子窗体的控件可以被编辑吗?

    当子窗体继承父窗体的控件时,控件左上角会有个小锁的图标,此时的子窗体不能再修改自己的属性,原来问题出在父窗体控件的属性(Modifiers),此时只需将属性值改为“Protected”,即可在子窗体上重新编辑控件。

(4)D层可以用泛型吗?

     第一次使用模板方法的时候,D层我使用的是泛型,结果就是可以查到记录的行数,却无法显示,标头显示的是实体名。

     那么D层可以用泛型吗?答案是可以,由于三个子窗体同时继承一个父窗体,因此需要有三个实体集供一个方法转换,此时datatable转泛型需要重写三次。


小结

    问题越多,收获越多。