实战模板方法模式——了解多态一点点

前言:

    学习了一个月的设计模式,终于派上了用场。在实现组合查询的三个窗体时,我机智的选择了模板方法模式,果断省了很多事情,并且体会到了一点点“多态”的味道。那是相当痛快啊~~

    现如今是一个拼爹的时代,一个好爹能让你至少少奋斗20年呢。(⊙o⊙)我为什么讲这个呢,其实多态也是这个道理。一个完美的父类,可以让N多个子类去继承它,这样就省掉了很多重复的代码,子类显得轻松很多。把重复的东西封装在父类,定义好一个框架一样的东西,然后通过不同的情况不同的结果,将具体的实现延迟到子类。这样说,你有没有想到今天的主题呢?先来了解一下我的模板方法是如何实现的。



一、模板方法的实现

    

    1、模板方法“3W1H”


      1W是什么:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。子类可以不改变一个算法的结构即可课重定义该算法的某些特定步骤。


      2W为什么用:①把不变行为移动到了超类,提供了一个很好的代码复用的平台。

               ②帮助子类摆脱重复的不变行为的纠缠。


      3W什么时候用:当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理。



    2、分析“变”与“不变”

    看过了3W,下面就来实际分析一下1H,看看在组合查询中如何实现它。先来分析一下三个窗体的共性和个性。

    共性:操作符一致,组合关系一致,组合查询字符串拼接框架一样(放到了model里封装),判断文本是否填全

    个性:字段名,表名,表格设计(字段和内容)



    3、代码实现(组合查询部分代码)

    

    把共性的东西都放在一起,封装成一个父窗体,把个性的东西都写成抽象的方法,让子类重写,实现多态性。这里把需要有更细节的变化的地方写成了抽象的方法(这里定义了三个抽象方法),在子窗体中具体的实现。


Imports Model
Imports BLL
'************************************************************
'类名称:frmCheck  
'命名空间:UI 
'功能:组合查询的父窗体,实现了组合查询中的基本的框架的定义
'创建时间:2014-10-21
'作者:周洲
'小组:**
'**************************************************************

Public Class frmCheck
    Protected group As New ModelGroup   '定义组合查询实体对象
    Protected Sub textInfo()             '定义数组维数
        ReDim Preserve arrayContorl(10)
        arrayContorl(0) = New Term(cboField1, "")
        arrayContorl(1) = New Term(cboField2, "")
        arrayContorl(2) = New Term(cboField3, "")
        arrayContorl(3) = New Term(cboOp1, "")
        arrayContorl(4) = New Term(cboOp2, "")
        arrayContorl(5) = New Term(cboOp2, "")
        arrayContorl(6) = New Term(txt1, "")
        arrayContorl(7) = New Term(txt2, "")
        arrayContorl(8) = New Term(txt3, "")
        arrayContorl(9) = New Term(cboZuhe1, "")
        arrayContorl(10) = New Term(cboZuhe2, "")
    End Sub
    Protected Sub frmGroupQuery_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        '设置表格控件的属性
        DataGridView1.AllowUserToAddRows = False
        DataGridView1.ReadOnly = True
        DataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect

        Dim i As Integer
        For i = 0 To DataGridView1.Columns.Count - 1
            DataGridView1.Columns(i).Width = DataGridViewAutoSizeColumnMode.AllCells
        Next

        '填写操作符的下拉框
        cboOp1.Items.Add(">")
        cboOp1.Items.Add("<")
        cboOp1.Items.Add("=")
        cboOp1.Items.Add("<>")

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

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

        '填写组合条件的下拉框
        cboZuhe1.Items.Add("与")
        cboZuhe1.Items.Add("或")

        cboZuhe2.Items.Add("与")
        cboZuhe2.Items.Add("或")
    End Sub

    Public Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
        '判断条件是否为空
        If cboZuhe1.Text.Trim() = "" Then
            If cboField1.Text.Trim() = "" Or cboOp1.Text.Trim() = "" Or txt1.Text.Trim() = "" Then
                MsgBox("查询条件不符合规定,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")
                Exit Sub
            End If
        End If

        If cboZuhe1.Text.Trim() <> "" Then
            If cboField1.Text.Trim() = "" Or cboOp1.Text.Trim() = "" Or txt1.Text.Trim() = "" Or
                    cboField2.Text.Trim() = "" Or cboOp2.Text.Trim() = "" Or txt2.Text.Trim() = "" Then
                MsgBox("查询条件不符合规定,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")
                Exit Sub
            End If
        End If

        If cboZuhe2.Text.Trim() <> "" Then
            If cboField1.Text.Trim() = "" Or cboOp1.Text.Trim() = "" Or txt1.Text.Trim() = "" Or
                    cboField2.Text.Trim() = "" Or cboOp2.Text.Trim() = "" Or txt2.Text.Trim() = "" Or
                    cboField3.Text.Trim() = "" Or cboOp3.Text.Trim() = "" Or txt3.Text.Trim() = "" Then

                MsgBox("查询条件不符合规定,请完善查询信息!", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "提示")

                Exit Sub
            End If

        End If


        group.TableName = GetTableName()   '给实体对象传表名
        group.Field1 = ToEnglish(cboField1.Text.Trim())  '给实体对象传字段名
        group.Field2 = ToEnglish(cboField2.Text.Trim())
        group.Field3 = ToEnglish(cboField3.Text.Trim())

        group.OP1 = cboOp1.Text.Trim()  '给实体对象传操作符
        group.OP2 = cboOp2.Text.Trim()
        group.OP3 = cboOp3.Text.Trim()

        group.TXT1 = txt1.Text.Trim()   '给实体对象传查询的内容
        group.TXT2 = txt2.Text.Trim()
        group.TXT3 = txt3.Text.Trim()

        group.ZU1 = ToEnglish(cboZuhe1.Text.Trim())   '给实体对象传组合条件
        group.ZU2 = ToEnglish(cboZuhe2.Text.Trim())



        Dim dt As New DataTable   '定义一个datatable并实例化
        Dim bllgroupquery As New BLLGroupQuery  '调用B层

        Call Togrid()   '调用填写表格的函数

    End Sub

    Public Overridable Function ToEnglish(cboName As String) As String
        '转换汉字到字段名
        Return ""
    End Function

    Public Overridable Function GetTableName() As String
        '获得表名
        Return ""
    End Function

    Public Overridable Sub Togrid()
        '填充表格
    End Sub
    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        '关闭窗体
        Me.Close()
    End Sub
    Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
        '清除文本
        Call textInfo()
        If AllClear(arrayContorl) Then
            cboField1.Focus()
            Exit Sub
        End If
    End Sub
End Class


    下面是子窗体的实现,继承了父窗体,重写了三个抽象的函数。这样把从U层获取的字段名、操作符、查询内容、组合关系、表名传给ModelGroup,在model层拼接好字符串,D层调用传到数据库中调数据。


Imports BLL
Imports Model
'************************************************************
'类名称:frmGroupOn   
'命名空间:UI 
'功能:查询上机记录,继承查询的父窗体,重写toenglish,gettablename,togrid
'创建时间:2014-10-21
'作者:周洲
'小组:**
'**************************************************************
Public Class frmGroupOn
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  
        cboField1.Items.Add("卡号")
        cboField1.Items.Add("姓名")
        cboField1.Items.Add("上机日期")
        cboField1.Items.Add("上机时间")
        cboField1.Items.Add("机器名")

        cboField2.Items.Add("卡号")
        cboField2.Items.Add("姓名")
        cboField2.Items.Add("上机日期")
        cboField2.Items.Add("上机时间")
        cboField2.Items.Add("机器名")

        cboField3.Items.Add("卡号")
        cboField3.Items.Add("姓名")
        cboField3.Items.Add("上机日期")
        cboField3.Items.Add("上机时间")
        cboField3.Items.Add("机器名")
    End Sub

    Public Overrides Function ToEnglish(cboName As String) As String
        Select Case cboName
            Case "卡号"
                ToEnglish = "cNo"
            Case "姓名"
                ToEnglish = "sName"
            Case "上机日期"
                ToEnglish = "lOnDate"
            Case "上机时间"
                ToEnglish = "lOnTime"
            Case "机器名"
                ToEnglish = "lPC"
            Case "与"
                ToEnglish = "and"
            Case "或"
                ToEnglish = "or"
            Case Else
                ToEnglish = ""
        End Select
    End Function

    Public Overrides Function GetTableName() As String
        ' 传数据库表名 
        Return "T_Line"
    End Function

    Public Overrides Sub Togrid()
        '把数据显示到datagridview中  
        Dim dt As New DataTable
        Dim GroupQueryBLL As New BLLGroupQuery

        Try
            dt = GroupQueryBLL.GroupQuery(group)

            If dt.Rows.Count = Nothing Then

                dt.Clear()
                DataGridView1.DataSource = Nothing
                DataGridView1.Refresh()
                Throw New Exception("没有符合条件的记录!")
            Else
                DataGridView1.DataSource = dt
                DataGridView1.Columns(0).Visible = False
                DataGridView1.Columns(1).HeaderText = "卡号"
                DataGridView1.Columns(2).Visible = False
                DataGridView1.Columns(3).Visible = False
                DataGridView1.Columns(4).HeaderText = "姓名"
                DataGridView1.Columns(5).Visible = False
                DataGridView1.Columns(6).Visible = False
                DataGridView1.Columns(7).HeaderText = "上机日期"
                DataGridView1.Columns(8).HeaderText = "上机时间"
                DataGridView1.Columns(9).HeaderText = "下机日期"
                DataGridView1.Columns(10).HeaderText = "下机时间"
                DataGridView1.Columns(11).HeaderText = "消费金额"
                DataGridView1.Columns(12).Visible = False
                DataGridView1.Columns(13).Visible = False
                DataGridView1.Columns(14).Visible = False
                DataGridView1.Columns(15).HeaderText = "机器名"
            End If
        Catch ex As Exception
            MsgBox("系统提示您:" + ex.Message, MsgBoxStyle.Information, "重构")
        End Try
    End Sub
End Class





    实践了模板方法模式,我认为最核心的是:重写父类方法,实现多态性。这里有一个容易混淆的概念,那就是重载。九期的师傅曾经问过我二者之间的异同,这里就顺便做个总结。


二、Overload 与 Override


    1、基本概念


    Overload(重载):如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载。

    Override(重写):如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写。


    

    2、对比

    

    二者都表现了类的多态性,但是细细看来,二者多态性的表现形式是不同的。重写的实质是子类对父类的继承实现多态性,让子类去重写父类的方法,具体的实现父类抽象的方法。比如:动物类有一个”吃“的方法,不同的动物实现吃这个方法是不一样的,猫吃鱼狗吃肉。重载的实质是同一个类当中,通过参数列表的改变来重载同一个方法。比如,一个求面积的类,三角形需要的是底和高,正方形需要边长,这样就要重载求面积的方法来实现不同形状的求面积的需求。


     更加细微的区别,总结在下面的表中。

重写与重载

Override(重写) Overload(重载)
参数列表 不能变 必须变:参数个数、类型、顺序
返回类型 不能变 可以变
访问权限 可降低,不能提升 可以变
抛出异常 可少,不可多 可以变
多态表现 父类,子类 同一个类中


    3、代码实现


   eg:override(重写)的例子

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>    '基类
    Public Class TheBase
        Public Overridable Sub Talk()
            MsgBox("What's up?")
        End Sub
    End Class
    '派生类
    Public Class TheDerived
        Inherits TheBase
        Public Overrides Sub Talk()
            MsgBox("I'm not the base.")
        End Sub
    End Class
 
使用
    Sub Main()
        Dim TestClass As TheDerived
        TestClass.Talk()
    End Sub
 
运行结果
 
对话框显示: I'm not the base.</strong></span>


    eg:override(重载)的例子

 Public Class TheClass
        Public Overloads Sub ToOverload(ByVal value As Integer)
            MsgBox("I got an integer:" & CStr(value))
        End Sub
        '重载
        Public Overloads Sub ToOverload(ByVal value As String)
            MsgBox("I got a string:" & value)
        End Sub
    End Class
使用
    Sub Main()
        Dim MyClass As TheClass
        MyClass.ToOverload(9527)
        MyClass.ToOverload("Hello World!")
    End Sub
运行结果
 
第一个对话框显示: I got an integer:9527
第二个对话框显示: I got a string:Hello World!


三、总结


    模板方法是比较常用的设计模式,在面向对象的主流中,继承和多态的实现,无疑是优化代码的好帮手。通过模板方法的使用,进一步认识了类的多态性;通过对比,又了解了多态性的不同的表现方法。机房真是越敲越有意思了,还剩最后一个上下机,加个策略模式如何?迫不及待的想马上去实现了。


你可能感兴趣的:(设计模式)