在机房收费系统中有三个地方用到了组合查询,分别是查看学生信息,产看上机信息,查看工作记录。这三个窗体中有大量重复的代码,只有少量的代码是不同的,鉴于此种情况,这次是重构了,就不要再向第一次那样复制粘贴了,这里我们可以用到一个设计模式——模板方法模式。
怎么用?
我们把三个窗体共性的东西抽象出一个窗体作为模板窗体,让其他三个窗体继承这个模板窗体,再分别加上自己个性化的东西就可以了。
注意事项:
一、模板是抽象出来的公共的窗体或者类,作为父类只是让其他子类来继承它,并不能作为三个组合查询窗体之一。因为在父类模板上的改动会延伸到子类。
二、在设计模板的过程中,我们要注意public、protected、 private的区别。
public任何人都可以使用。
protected只有父亲和父亲的传人可以用(父类以及继承父类的子类)
private仅有父亲自己可以用(只有父类可以调用)
三、注意Overridable关键字,模板中方法都要就加Overridable关键字,意思是可重写的,然后到子类中去重写这个方法。
具体实例:
父类窗体代码:
<span style="font-size:18px;"><strong> Public comboquery As New Entity.ComboQueryEntity '实例化组合查询实体 Public Overridable Sub frmGroupQueryPrent_Load(sender As Object, e As EventArgs) Handles MyBase.Load MaximizeBox = False '不显示最大化按钮 '将参数传给实体,赋初值 '不同窗体字段不同,赋值为"",子窗体重写 comboquery.Field1 = "" comboquery.Field2 = "" comboquery.Field3 = "" '操作符 ComboOper1.Items.Add(">") ComboOper1.Items.Add("<") ComboOper1.Items.Add("=") ComboOper1.Items.Add("<>") ComboOper2.Items.Add(">") ComboOper2.Items.Add("<") ComboOper2.Items.Add("=") ComboOper2.Items.Add("<>") ComboOper3.Items.Add(">") ComboOper3.Items.Add("<") ComboOper3.Items.Add("=") ComboOper3.Items.Add("<>") ComboGroup1.Items.Add("与") ComboGroup1.Items.Add("或") ComboGroup2.Items.Add("与") ComboGroup2.Items.Add("或") '窗体加载后,后两组控件默认不可用 ComboField2.Enabled = False ComboOper2.Enabled = False txtContent2.Enabled = False ComboGroup2.Enabled = False ComboField3.Enabled = False ComboOper3.Enabled = False txtContent3.Enabled = False '设置选中单元格即选中行 DGV1.SelectionMode = DataGridViewSelectionMode.FullRowSelect Dim i As Integer '调整列宽 For i = 0 To DGV1.Columns.Count - 1 DGV1.Columns(i).Width = DataGridViewAutoSizeColumnsMode.AllCells Next End Sub</strong></span>
父类查询代码:
<span style="font-size:18px;"><strong> Public Overridable Sub cmdQuery_Click(sender As Object, e As EventArgs) Handles cmdQuery.Click '判断组合框不为空 If ComboGroup1.Text = "" Then If ComboField1.Text = "" Or ComboOper1.Text = "" Or txtContent1.Text = "" Then MsgBox("第一行查询条件不能为空", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示") Exit Sub End If End If If ComboGroup1.Text <> "" Then If ComboField2.Text = "" Or ComboOper2.Text = "" Or txtContent2.Text = "" Then MsgBox("第二行查询条件不能为空", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示") Exit Sub End If Else If ComboGroup2.Text <> "" Then If ComboField3.Text = "" Or ComboOper3.Text = "" Or txtContent3.Text = "" Then MsgBox("第三行查询条件不能为空", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示") Exit Sub End If End If End If '将参数传给实体 comboquery.dbName = GetdbName() comboquery.Field1 = ToEnglish(ComboField1.Text) comboquery.Field2 = ToEnglish(ComboField2.Text) comboquery.Field3 = ToEnglish(ComboField3.Text) comboquery.Operator1 = ComboOper1.Text.Trim comboquery.Operator2 = ComboOper2.Text.Trim comboquery.Operator3 = ComboOper3.Text.Trim comboquery.Content1 = txtContent1.Text.Trim comboquery.Content2 = txtContent2.Text.Trim comboquery.Content3 = txtContent3.Text.Trim comboquery.Relation1 = ToEnglish(ComboGroup1.Text) comboquery.Relation2 = ToEnglish(ComboGroup2.Text) '调用外观层方法 Dim cboll As New Facade.ComboQueryFac Dim table As New DataTable '没有返回结果 If cboll.ComboQuery(comboquery) Is Nothing Then MsgBox("没有记录", vbOKOnly, vbExclamation) DGV1.DataSource = Nothing Else '将结果返回给datatable table = cboll.ComboQuery(comboquery) DGV1.DataSource = table End If End Sub</strong></span>
需自己定义的方法:
<span style="font-size:18px;"><strong> '定义虚函数ToEnglis,将查询字段转化为数据库字段 Public Overridable Function ToEnglish(cboName As String) As String Return "" End Function '获得数据库表名 Public Overridable Function GetdbName() As String Return "" End Function</strong></span>
这里要说的是,写完模板窗体的U层之后,接着往下写就行了一直写到D层,跟其他窗体没有什么区别。只是D层我用了存储过程写的查询语句。
子类实现:
以查看上机信息为例:
<span style="font-size:18px;"><strong>Private Sub frmLineRecord_Load(sender As Object, e As EventArgs) Handles MyBase.Load ComboField1.Items.Add("卡号") ComboField1.Items.Add("消费时间") ComboField1.Items.Add("消费金额") ComboField1.Items.Add("上机日期") ComboField1.Items.Add("上机时间") ComboField1.Items.Add("下机日期") ComboField1.Items.Add("下机时间") ComboField2.Items.Add("卡号") ComboField2.Items.Add("消费时间") ComboField2.Items.Add("消费金额") ComboField2.Items.Add("上机日期") ComboField2.Items.Add("上机时间") ComboField2.Items.Add("下机日期") ComboField2.Items.Add("下机时间") ComboField3.Items.Add("卡号") ComboField3.Items.Add("消费时间") ComboField3.Items.Add("消费金额") ComboField3.Items.Add("上机日期") ComboField3.Items.Add("上机时间") ComboField3.Items.Add("下机日期") ComboField3.Items.Add("下机时间") End Sub Public Overrides Function ToEnglish(cbofield As String) As String Select Case cbofield Case "卡号" ToEnglish = "CardID" Case "消费时间" ToEnglish = "Consumetime" Case "消费金额" ToEnglish = "Consume" Case "上机日期" ToEnglish = "Ondate" Case "上机时间" ToEnglish = "Ontime" Case "下机日期" ToEnglish = "Offdate" Case "下机时间" ToEnglish = "Offtime" Case "与" ToEnglish = "and" Case "或" ToEnglish = "or" Case Else ToEnglish = "" End Select End Function Public Overrides Function GetdbName() As String Return "T_Line" End Function</strong></span>
组合查询说到这里就差不多了,关于子窗体如何继承父窗体的问题,大家可以去看这篇博客,讲的非常详细窗体继承。
分析一下我觉得我做的很不好的地方,我这里没有用到泛型,因为这里涉及到的实体比较多,当时考虑到可能会报错比较多,就没有用泛型,如果使用泛型的话,在U层和D层都要多加一些判断,去判断不同的子窗体要分别取对应哪个泛型集合,也不是太难。
重构的过程,确确实实是一个收获的过程,继续加油!