背景:在机房收费系统中,有好多基本上一样的窗体。比如:查看学生充值记录与学生上机记录、金额收取信息查询与金额退还信息查询,还有组合查询中:学生信息维护、操作员工作记录、学生上机信息统计。在这个时候,为了提高效率,我们应该怎样去做呢?
一、模版方法概述
模版方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
有时,我们知道一个算法需要的步骤和顺序,但是每个算法的具体实现不同。比如在机房收费系统中,组合查询有三个窗体类,每个窗体类的算法结构相同,具体实现不同,此时我们可以使用模版方法。
模版方法模式 把我们不知道具体实现的步骤封装成抽象方法,提供一个按正确顺序调用他们的具体方法,构成一个抽象基类,子类继承抽象基类实现各个步骤的抽象方法,工作流程由抽象基类控制。把实现推迟到子类中,通过继承来达到对象的复用,同时遵守了开闭原则。
二、实战演练
1、首先,我们需要建立一个模版窗体
2、然后需要建立子窗体进行继承(我们以“学生信息维护”为例)
①创建继承窗体
②选择要继承哪个窗体
这样,”学生信息维护“的组合查询就建好了。
3、实现代码
U层
'**********************************************
' 文 件 名:frmGroupQuery
' 命名空间:UI
' 内 容:
' 功 能:组合查询的模版窗体
' 文件关系:
' 作 者:杨晨光
' 小 组:
' 生成日期:2014/8/25 14:14:37
' 版 本 号:V1.0.0.0
' 修改日志:
' 版权说明:
'**********************************************
Public Class frmGroupQuery
'实例化一个新的实体对象,
Protected EGroup As New Entity.GroupQueryEntity
'窗体加载
Private Sub frmGroupQuery_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'窗体加载时,给字段、操作符、组合关系赋值
'加载组合关系
cmbRelation1.Items.Add("并且")
cmbRelation2.Items.Add("并且")
cmbRelation1.Items.Add("或者")
cmbRelation2.Items.Add("或者")
'给操作符赋值
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("<>")
'窗体加载后,后两行文本框不能使用
cmbFields2.Enabled = False
cmbOperator2.Enabled = False
txtContext2.Enabled = False
cmbFields3.Enabled = False
cmbOperator3.Enabled = False
txtContext3.Enabled = False
End Sub
'单击查询按钮
Private Sub btnCheck_Click(sender As Object, e As EventArgs) Handles btnCheck.Click
'先将DataGridView中清空
DataGridView.DataSource = Nothing
'判断文本框、组合框是否为空
'第一个组合关系为空时,第一行的都不能为空
If cmbRelation1.Text = "" Then
Dim arrayControl() As Control
ReDim Preserve arrayControl(2)
arrayControl(0) = cmbFields1
arrayControl(1) = cmbOperator1
arrayControl(2) = txtContext1
'调用模块,来进行判断是否为空
If IsEmpty.IsAllEmptyText(arrayControl) Then
Exit Sub
End If
End If
'第一个组合关系文本框不为空,则判断前两行不能为空
If cmbRelation1.Text <> "" Then
Dim arrayControl() As Control
ReDim Preserve arrayControl(5)
arrayControl(0) = cmbFields1
arrayControl(1) = cmbOperator1
arrayControl(2) = txtContext1
arrayControl(3) = cmbFields2
arrayControl(4) = cmbOperator2
arrayControl(5) = txtContext2
'调用模块,来进行判断是否为空
If IsEmpty.IsAllEmptyText(arrayControl) Then
Exit Sub
End If
End If
'第二个组合关系文本框不为空,判断所有的不为空
If cmbRelation2.Text <> "" Then
Dim arrayControl() As Control
ReDim Preserve arrayControl(8)
arrayControl(0) = cmbFields1
arrayControl(1) = cmbOperator1
arrayControl(2) = txtContext1
arrayControl(3) = cmbFields2
arrayControl(4) = cmbOperator2
arrayControl(5) = txtContext2
arrayControl(6) = cmbFields3
arrayControl(7) = cmbOperator3
arrayControl(8) = txtContext3
'调用模块,来进行判断是否为空
If IsEmpty.IsAllEmptyText(arrayControl) Then
Exit Sub
End If
End If
'给实体赋值(需要在实体层新建一个实体类)
EGroup.Fields1 = GetDBName(cmbFields1.Text.Trim())
EGroup.Fields2 = GetDBName(cmbFields2.Text.Trim())
EGroup.Fields3 = GetDBName(cmbFields3.Text.Trim())
EGroup.Operator1 = cmbOperator1.Text.Trim()
EGroup.Operator2 = cmbOperator2.Text.Trim()
EGroup.Operator3 = cmbOperator3.Text.Trim()
EGroup.Context1 = txtContext1.Text.Trim()
EGroup.Context2 = txtContext2.Text.Trim()
EGroup.Context3 = txtContext3.Text.Trim()
EGroup.Relation1 = GetDBName(cmbRelation1.Text.Trim())
EGroup.Relation2 = GetDBName(cmbRelation2.Text.Trim())
EGroup.GetList = GetList()
'调用B层的方法,进行传递参数
Dim BGroup As New BLL.GroupQueryBLL '实例化B层的对象
Dim dt As New DataTable
Call Dview()
End Sub
'组合关系A文本框是否有值,会影响到下边行的能否操作的问题
Private Sub cmbRelation1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbRelation1.SelectedIndexChanged
'组合关系A中没有东西
If cmbRelation1.Text <> "" Then
cmbFields2.Enabled = True
cmbOperator2.Enabled = True
txtContext2.Enabled = True
End If
End Sub
'组合关系B文本框是否有值,会影响到下边行的能否操作的问题
Private Sub cmbRelation2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbRelation2.SelectedIndexChanged
'组合关系B中没有值
If cmbRelation2.Text <> "" Then
cmbFields3.Enabled = True
cmbOperator3.Enabled = True
txtContext3.Enabled = True
End If
End Sub
' 定义虚函数GetDBName,获取不同数据库的字段名
Protected Overridable Function GetDBName(ByVal control As String) As String
Return ""
End Function
' 定义虚函数GetList,获取不同数据库的表名
Protected Overridable Function GetList() As String
Return ""
End Function
'定义虚函数Dview,将数据显示到控件中
Protected Overridable Sub Dview()
End Sub
'导出到Excel表
Private Sub btnLead_Click(sender As Object, e As EventArgs) Handles btnLead.Click
Call ImportToExcel.ImportToExcel(DataGridView) '调用的导出Excel表的模块
End Sub
End Class
Public Class GroupQueryBLL
Function Query(ByVal user As Entity.GroupQueryEntity) As DataTable
Dim fty As New AbsFactory.DataAccess '实例化一个新的工厂对象
Dim IGroup As IDAL.IGroupQuery '定义一个接口函数
'Dim myList As List(Of Entity.GroupQueryEntity) '定义一个泛型集合,用来承接返回值
Dim dt As DataTable
IGroup = fty.CreateIGroupQuery
dt = IGroup.Query(user)
Return dt
End Function
End Class
工厂层
Public Class DataAccess
'通过配置文件获取连接字符串并赋值给所声明的变量strConnection
Dim strConnection As String = ConfigurationManager.AppSettings("DALName")
'组合查询表
Public Function CreateIGroupQuery() As IDAL.IGroupQuery
Return CType(Assembly.Load(strConnection).CreateInstance(strConnection + "." + "GroupQueryDAL"), IDAL.IGroupQuery)
End Function
End Class
接口层
Public Interface IGroupQuery
'定义一个组合查询的接口函数,,,返回值为DataTable
Function Query(ByVal user As Entity.GroupQueryEntity) As DataTable
End Interface
D层
Public Class GroupQueryDAL
: Implements IGroupQuery
'定义组合查询的函数
Public Function Query(user As Entity.GroupQueryEntity) As DataTable Implements IGroupQuery.Query
'定义一个数据库操作的助手类
Dim MySqlHelper As New SqlHelper
'定义一个数据库操作的语句
Dim strSql As String
'调用存储过程
strSql = "PROC_GroupQuery"
'加入参数
Dim paras As SqlParameter() = {New SqlParameter("@Fields1", user.Fields1),
New SqlParameter("@Fields2", user.Fields2),
New SqlParameter("@Fields3", user.Fields3),
New SqlParameter("@Operator1", user.Operator1),
New SqlParameter("@Operator2", user.Operator2),
New SqlParameter("@Operator3", user.Operator3),
New SqlParameter("@Context1", user.Context1),
New SqlParameter("@Context2", user.Context2),
New SqlParameter("@Context3", user.Context3),
New SqlParameter("@Relation1", user.Relation1),
New SqlParameter("@Relation2", user.Relation2),
New SqlParameter("@GetList", user.GetList)}
'定义一个dt,用来保存执行结果的值
Dim dt As DataTable
dt = MySqlHelper.ExecSelect(strSql, CommandType.StoredProcedure, paras)
Return dt
End Function
End Class
存储过程
-- =============================================
-- Author: 杨晨光>
-- Create date: <Create Date,,>
-- Description: 组合查询
-- =============================================
ALTER PROCEDURE [dbo].[PROC_GroupQuery]
--加入参数
@Fields1 varchar(10),
@Fields2 varchar(10),
@Fields3 varchar(10),
@Operator1 varchar(10),
@Operator2 varchar(10),
@Operator3 varchar(10),
@Context1 varchar(20),
@Context2 varchar(20),
@Context3 varchar(20),
@Relation1 varchar(10),
@Relation2 varchar(10),
@GetList varchar(20) --获取表的名称
AS
declare @TempSql varchar(500)--临时存放sql语句
BEGIN
SET @TempSql='SELECT * FROM '+@GetList +' WHERE ' +@Fields1 +@Operator1+char(39) + @Context1 + char(39)
if @Relation1 != '' --第一个组合关系文本框不为空
BEGIN
SET @TempSql=@TempSql+@Relation1+CHAR(32)+@Fields2 +@Operator2+CHAR(39)+@Context2+CHAR(39)
if @Relation2 != '' --第二个组合关系文本框不为空
BEGIN
SET @TempSql=@TempSql+@Relation2 +CHAR(32)+@Fields3+@Operator3+CHAR(39)+@Context3+CHAR(39)
END
END
EXECUTE(@TempSql)
END
以上是父窗体的代码(实体层就不写了),下边是子类(学生信息维护)窗体的代码
Imports System.Collections '引入哈希表
Public Class frmSInfoMaintain
Private Sub frmSInfoMaintain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'表名
Me.Text = "学生基本信息维护"
Dim haField As New Hashtable '定义处理字段名转换的hashtable
'使用哈希表叫数据库中的字段进行转换
Dim FieldName() As String
Dim FieldValue() As String
'数据库中的字段对应相应的中文
FieldName = {"卡号", "学号", "姓名", "性别", "系别", "年级", "班级"}
FieldValue = {"userNo", "studentID", "studentName", "Sex", "Department", "Grage", "Class"}
'放到Field字段中
cmbFields1.Items.AddRange(FieldName)
cmbFields2.Items.AddRange(FieldName)
cmbFields3.Items.AddRange(FieldName)
'中文为key,英文为value添加到hashtable
For i As Integer = 0 To FieldName.Count - 1
haField.Add(FieldName(i), FieldValue(i))
Next
End Sub
'重写获得表名方法
Protected Overrides Function GetList() As String
Return "T_StudentInfo"
End Function
'重写转换成数据库字段方法
Protected Overrides Function GetDBName(ByVal control As String) As String
Select Case (control)
Case "卡号"
Return "cardNo"
Case "学号"
Return "studentID"
Case "姓名"
Return "studentName"
Case "性别"
Return "Sex"
Case "系别"
Return "Department"
Case "年级"
Return "Grade"
Case "班级"
Return "Class"
Case "并且"
Return "and"
Case "或者"
Return "or"
Case Else
Return ""
End Select
End Function
Protected Overrides Sub Dview()
'每次数据查询,情况表
DataGridView.DataSource = Nothing
'表格控件绑定数据源字段
'调用B层的方法,进行传递参数
Dim BGroup As New BLL.GroupQueryBLL '实例化B层的对象
Dim dt As New DataTable
dt = BGroup.Query(EGroup)
If dt.Rows.Count = 0 Then
MsgBox("没有记录,请重新进行查询!", MsgBoxStyle.Exclamation, "警告")
DataGridView.DataSource = Nothing
Else
DataGridView.DataSource = dt
DataGridView.DataSource = dt
DataGridView.Columns(0).HeaderText = "卡号"
DataGridView.Columns(1).HeaderText = "学号"
DataGridView.Columns(2).HeaderText = "姓名"
DataGridView.Columns(3).HeaderText = "性别"
DataGridView.Columns(4).HeaderText = "系别"
DataGridView.Columns(5).HeaderText = "年级"
DataGridView.Columns(6).HeaderText = "班级"
DataGridView.Columns(7).Visible = False
DataGridView.Columns(8).Visible = False
DataGridView.Columns(9).Visible = False
DataGridView.Columns(10).Visible = False
DataGridView.Columns(11).Visible = False
'删除最下边的一行
DataGridView.AllowUserToAddRows = False
End If
End Sub
End Class
三、反思
使用模版方法实现组合查询虽然实现了,但是其中还是有一些不足。例如:层与层之间传递数据使用的DataTable,我曾经尝试使用泛型集合来进行传递,可是没能成功,所以最后只能妥协使用DataTable。在子窗体进行继承的时候,使用了哈希表,减少了部分代码。虽然使用了哈希表,但是对它的了解还是很模糊,还有待于去钻研。
记住:多思考,多总结。