物件導向程式簡介

2018年12月02日

首頁

 

第十九章物件導向程式設計簡介

19.1前言

物件導向程式設計,是程式設計界熱烈話題、主流,觀念比較難懂抽象,當程式編輯者如已熟悉基本的程式設計作業工作,想要進一步提昇程式設計能力的深度及廣度時,就必須熟諳物件導向程式設計的『觀念及方法』。物件導向本身使用很多抽象的「方法及理論」,而不再是只專注於程式設計的實質內容。VB.Net 是一種物件導向程式(Object-Oriented Programming,OOP)設計語言。物件導向程式設計是程式設計的一種方式,在物件導向程式設計中,程式設計師可以用類別(Class)區塊,將相關的屬性(Property)、方法(Methods,函數、程式)、事件(Events)等程式碼包夾在程式命名空間(NameSpace,資料圖書館)區塊中,稱之為「類別」,所以程式設計師可以設計許多的類別,以便於日後重複使用,或將其封裝成為動態資料連接庫(Dll)供他人使用。OOP程式設計的有三大特性:(1)封裝(Encapsulation)(2)繼承(Inheritance)(3)多態或多型(Polymorphism)等,可以讓程式維護、修改、擴充更容易。本章與下一章均會討論與類別有關的內容,本章將著重於一般名詞及初級觀念的解釋,下一章則較偏重實際內容的討論。

19.2封裝

封裝(Encapsulation)有隱藏資料的意涵,將不需要公開的資料宣告為私有,不讓外部存取,以避免不必要的修改,也使程式的分工、維護、擴充更容易。封裝後之程式碼可以重複使用,也可以供多數人使用。如下面的程式碼都是計算矩形面積的程式,功用相同,但資料的宣告及存取方法有別。觀察下面的程式碼,程式(1)未使用類別,直接呼叫使用帶有引數的私有函數(FunctionareaT()),私有函數的引數直接包夾在areaT()中,直接傳遞引數,(2)則利用公有類別、公有引數、函數area2(),利用建立物件方式直接傳遞公有引數,(3)利用公有類別、建構式(New)、函數area3(),將引數宣告為私有,透過帶有引數的建構式引數傳遞與呼叫程式溝通、對口,(4)則利用公有類別、公有get_set屬性程序、函數area4(),引數與(3)一樣仍然宣告為私有,透過get_set屬性程序,以間接方式傳遞。(3)(4)將引數_width_height宣告為Private,透過關鍵字New()get_set屬性與外部溝通,因此(3)(4)均有封裝的特性,引數傳遞為間接方式,程式運用有點像在黑箱中作業。

(1)直接呼叫私有函數 areaT(引數)

'Method1

Private Sub btnClassArea1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClassArea1.Click

        Dim myWidth = InputBox("請輸入長方形寬度"'取得變數width

        Dim myHeight = InputBox("請輸入長方形高度")

        Dim area1 = areaT(myWidth, myHeight)

        MsgBox("Method1:area= " & area1)

    End Sub

 

    Private Function areaT(ByVal wid As Double, ByVal ht As Double) As Double

        If wid <= 0 Or ht <= 0 Then

            MsgBox("輸入資料有誤,請檢查後重來)")

            Exit Function

        Else

            Return wid * ht

        End If

    End Function

(2)利用公有類別、公有引數、函數area2(無引數)

'Method2

Private Sub btnClassArea2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClassArea2.Click

        Dim myWidth = InputBox("請輸入長方形寬度"'取得變數width

        Dim myHeight = InputBox("請輸入長方形高度") '取得ClsArea公有變數height

        Dim B As New ClsArea2

        B.width = myWidth

        B.height = myHeight

        Dim area2 As Double = B.area2()

        MsgBox("Method2:public width,height, area= " & area2)

    End Sub

 

Public Class ClsArea2

    Public width As Double

    Public height As Double

 

    Public Function area2() As Double

        '傳回 _width*_height

        Return width * height

    End Function

End Class

(3)利用公有類別、建構式(New(引數))、函數area3()

'Method3

Private Sub btnClassArea3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClassArea3.Click

        Dim myWidth = InputBox("請輸入長方形寬度"'取得變數width

        Dim myHeight = InputBox("請輸入長方形高度") '取得ClsArea公有變數height

        Dim A As New ClsArea3(myWidth, myHeight)

        Dim area3 As Double = A.area3

        MsgBox("Method3:private _width,_height,New(width,ht),area=Area= " & area3)

    End Sub

 

Public Class ClsArea3

    Private _width As Double

    Private _height As Double

    Private _errNo As Integer

    Public Sub New(ByVal width As Double, ByVal ht As Double)

        If width <= 0 Or ht <= 0 Then

            MsgBox("資料輸入有誤,請檢查後重來")

            _errNo = -1

            Exit Sub

        Else

            _errNo = 0

            _width = width

            _height = ht

        End If

    End Sub

    Public Function area3() As Double

        If _errNo < 0 Then

            MsgBox("資料輸入有誤,請檢查後重來")

            Return -1

        Else

 

            Return _width * _height

        End If

    End Function

 

End Class

(4)利用公有類別、公有get_set屬性程序、函數area4()

'Method4

Private Sub butClassArea4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butClassArea4.Click

        Dim myWidth = InputBox("請輸入長方形寬度"'取得變數width

        Dim myHeight = InputBox("請輸入長方形高度") '取得ClsArea公有變數height

        Dim A As New ClsArea4

        Dim myError As Double

        A.width = myWidth      '透過ClsArea2屬性width取得tWidth

        A.height = myHeight    '透過ClsArea2屬性height取得theight

        myError = A.width   '如為負值代表輸入資料錯誤

        myError = myError + A.height  '如為負值代表輸入資料錯誤

        If myError < 0 Then

            MsgBox("Error= " & myError & " 資料輸入有誤,請檢查後重來")

            Exit Sub

        Else

           Dim Area4 As Double = A.area4()          '注意!!!原形函數area()並無引數

            MsgBox("Method4:private _width,_height,New(),area=Area= " & Area4)

        End If

    End Sub

End Class

 

Public Class ClsArea4

    Private _width As Double

    Private _height As Double

   

    Public Property width() As Double  '設定width屬性

        Get

            Return _width

        End Get

        Set(ByVal value As Double)

            If value < 0 Then

                MsgBox("輸入的寬度資料為負值??!!,請重來")

                _width = -1.0E+30      '輸入負值後以-1.0E+30代表

                Exit Property

            Else

                _width = value

            End If

        End Set

    End Property

 

    Public Property height() As Double  '設定height屬性

        Get

            Return _height

        End Get

        Set(ByVal value As Double)

            If value < 0 Then

                MsgBox("輸入的長度資料為負值??!!,請重來")

                _height = -1.0E+30    '輸入負值後以-1.0E+30代表

                Exit Property

            Else

                _height = value

            End If

        End Set

    End Property

    Public Function area4() As Double

        '傳回 _width*_height

        Return _width * _height

    End Function

End Class

19.3繼承

類別Class可透過Inheritance(繼承)來繼承原有類別所有Public屬性、方法及事件,如有需要也可以修改或擴充Class的內容。可以被繼承的原始類別稱為「基礎Class」或「父Class」,繼承基礎Class的新Class稱為「衍生Class」或「Class」。衍生Class會繼承基礎Class的「Public成員」,然後依需求直接使用或是改寫基礎Class的功能與擴充衍生其他功能。.NET Framework繼承關係只會向下傳遞,而且.NET Framework不支援多重繼承,每一個Class只能繼承一個ClassVB.NET中的視窗表單(WinForm)、控制項等都是一種類別,因此它都可以拿來作為基礎父類別,下面的程式碼及執行成果就是利用繼承VB.NET的文字方塊類別的最簡單繼承例子。

 

Public Class Form2

    Class TextBox '類別TextBox

        Inherits System.Windows.Forms.TextBox '宣告繼承自WinFormTextBox類別

    End Class

 

    Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim txt1 As New TextBox '宣告txt1 TextBox類別

        Me.Controls.Add(txt1)  'Me指作用中表單,自動加入TextBox

        txt1.Multiline = True

        txt1.BorderStyle = BorderStyle.FixedSingle 'TextBox外框

        txt1.Left = 100

        txt1.Top = 100

        txt1.Width = 100

        txt1.Height = 30

        txt1.Text = "VB.NET" 'txt1書寫"VB.NET"

    End Sub

End Class

 

下圖為本程式之執行成果

 

19.3.1父類別細化及子類別抽離

要探討基礎父類別與衍生子類別的繼承關係,可以兩種不同方式來觀察。如果由「上、下關係」來說,則衍生子類別為父類別的細化(Refinement),父類別細化後的衍生子類別項目特性是基礎父類別所沒有的。如以哺乳動物(Mammal animal)為父類別為例,他們共同的特徵有:溫血、哺乳、有腳、會動、會吃、會叫……等,人類是哺乳動物中的一種,如人類視為哺乳動物的衍生類別,則人類特有的『會說話、會笑、會哭、會寫字、會計算.』等特性就是類別哺乳動物-人類類別的細化項目。

另一方面從「下、上關係」來說,是將子類別中有共同特徵,全部抽離彙集(Abstraction)起來就可以組成基礎父類別,這種抽離彙集作業亦稱之為共同化(Generalization)。以繪圖程式來說,您可以定義規範一個可以畫點、線(直線、弧)、面(矩形、多邊形、園、橢圓)、組合圖形(多種圖元組合)的畫圖類別。這些畫圖物件中如線條顏色、線寬(Pens)、塗抹顏色(Brushes),為這些類別的共同特性,因此可以將其抽離出來作為基礎類別;而所有圖元其外框都可以以矩形為邊界包含,因此其也可以被抽離出來作為基礎類別。您可以將這些共同的屬性或方法抽離後定義一個名稱為「Drawable」基礎父類別,在Drawable中定義所有圖形的邊界矩形為單一變數Rectangle,將畫圖的畫筆、筆刷各別定義成類別。同時定義DrawObject為可以畫所有圖形的方法(程序),在DrawObject畫圖程序前用MustOverrides關鍵字將DrawObject定義為抽象方法,其實作必須藉DrawObject所有衍生類別來執行完成,在Drawable類別定義其應有的參數。如此將共同物件抽離後,所有子類別間重複及共同的程式碼就可以刪除精簡,這就是利用OOP的好處。為能讓人清楚瞭解衍生類別及基礎類別的繼承關係,可以將Drawable類別的衍生類別物件名稱,在Drawable後加 寫圖元名稱如:DrawableLineDrawableEllipseDrawableRectangleDrawablePolylineDrawablePolygon,如此不但能讓人清楚瞭解其相互間繼承關係,也不會與命名空間提供的繪圖方法混淆。為方便圖元的存取,也可以在Drawable基礎類別中另定義一個DrawAllObject來存取所有畫在繪圖物件上的圖元。如下面的片段程式碼:(1)抽象基礎類別為Drawable(以關鍵字MustInherit修飾)Drawable中屬性及方法多宣告為Public,抽象基礎類別Drawable中包含抽象方法Sub DrawFunction GetBounds (修飾字MustOverride)(2)基礎類別DrawableAllObject(3)衍生類別DrawableLine(繼承自Drawable),因此DrawableLine中方法Sub DrawFunction GetBounds均必須使用關鍵字Overrides修飾相互呼應。有關抽象基礎類別Drawable相關說明及程式編碼部分請參考Rod Stephens所著Visual Basic 2010 Programmers Reference25章。

 

(a)抽象基礎類別Drawable

Public MustInherit Class Drawable 抽象基礎類別

    '繪圖屬性.

    Public ForeColor As Color

    Public FillColor As Color

    Public LineWidth As Integer = 0

    .

.

.

    ' 建構式.

    Public Sub New() '無引數建構式

        ForeColor = Color.Black

        FillColor = Color.White

    End Sub

 

    Public Sub New(ByVal fore_color As Color, ByVal fill_color As Color, Optional ByVal line_width As Integer = 0) '有引數建構式,以此和衍生Class對口

 

        LineWidth = line_width

        ForeColor = fore_color

        FillColor = fill_color

    End Sub

 

    ' 前景色屬性.

    Public Property ForeColorArgb() As Integer

        Get

            Return ForeColor.ToArgb()

        End Get

        Set(ByVal Value As Integer)

            ForeColor = Color.FromArgb(Value)

        End Set

    End Property

 

    '背景色屬性.

    Public Property FillColorArgb() As Integer

        Get

            Return FillColor.ToArgb()

        End Get

        Set(ByVal Value As Integer)

            FillColor = Color.FromArgb(Value)

        End Set

    End Property

 

   ' 在繪圖物件上繪圖.

    Public MustOverride Sub Draw(ByVal gr As Graphics) 抽象方法

 

    ' 矩形邊界.

    Public MustOverride Function GetBounds() As Rectangle 抽象方法

 

    .

    .  

    .

 End Class

 

    Public Property BackColorArgb() As Integer

 

(b)基礎類別DrawableAllObject

 

Public Class DrawableAllObject

   Public Drawables As New List(Of Drawable)

 

    ' 背景色The background color.

    Public BackColor As Color = SystemColors.Control

    Public Property BackColorArgb() As Integer

        Get

            Return BackColor.ToArgb()

        End Get

        Set(ByVal Value As Integer)

            BackColor = Color.FromArgb(Value)

        End Set

    End Property

 

    ' 建構式Constructors.

    Public Sub New()

    End Sub

    Public Sub New(ByVal background_color As Color)

        BackColor = background_color

    End Sub

.

.

.

    Public Property SelectedDrawable() As Drawable

        Get

            Return m_SelectedDrawable

        End Get

        Set(ByVal Value As Drawable)

            '如果不是所選取的圖元者 .

            If Not (m_SelectedDrawable Is Nothing) Then

                m_SelectedDrawable.IsSelected = False

            End If

 

            ' 選取新物件.

            m_SelectedDrawable = Value

            If Not (m_SelectedDrawable Is Nothing) Then

                m_SelectedDrawable.IsSelected = True

            End If

        End Set

    End Property

   .

   .

   .

    ' 畫圖元物件.

    Public Sub Draw(ByVal gr As Graphics)

        ' 清楚背景.

        gr.Clear(BackColor)

        gr.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

 

        ' 繪製物件.

        For Each dr As Drawable In Drawables

            dr.Draw(gr)

        Next dr

    End Sub

 

    .

    .

    .

 

       ' 回傳畫圖物件邊界.

    Public Function GetBounds() As Rectangle

        If Drawables.Count < 1 Then Return (New Rectangle(0, 0, 0, 0))

        Dim result As Rectangle = Drawables(0).GetBounds()

        For Each dr As Drawable In Drawables

            result = Rectangle.Union(result, dr.GetBounds())

        Next dr

 

        Return result

    End Function

End Class

 

(c)衍生類別DrawableLine

Imports System.Math

Public Class DrawableLine

    Inherits Drawable

    ' 建構式.

    Public Sub New()

    End Sub

 

    Public Sub New(ByVal fore_color As Color, Optional ByVal line_width As Integer = 0, Optional ByVal new_x1 As Integer = 0, Optional ByVal new_y1 As Integer = 0, Optional ByVal new_x2 As Integer = 1, Optional ByVal new_y2 As Integer = 1) 以此與呼叫程式(主程式溝通對口)

        MyBase.New(fore_color, Nothing, line_width) 以此和基礎Class對口

        X1 = new_x1

        Y1 = new_y1

        X2 = new_x2

        Y2 = new_y2

    End Sub

 

    ' 在繪圖物件上繪圖.

    Public Overrides Sub Draw(ByVal gr As System.Drawing.Graphics)

      Do something

    End Sub

 

    ' 回傳矩形邊界.

    Public Overrides Function GetBounds() As System.Drawing.Rectangle

        Return New Rectangle( _

            Min(X1, X2), _

            Min(Y1, Y2), _

            Abs(X2 - X1), _

            Abs(Y2 - Y1))

    End Function

     .

     .

.

.

 

    ' 移動新點.

    Public Overrides Sub NewPoint(ByVal x As Integer, ByVal y As Integer)

        X2 = x

        Y2 = y

    End Sub

 

    ' 如果是空物件回傳真值.

    Public Overrides Function IsEmpty() As Boolean

        Return (X1 = X2) AndAlso (Y1 = Y2)

    End Function

End Class

     上面所介紹之程式碼係節錄自Rod Stephens所著Visual Basic 2010 Programmers Reference25章,原始碼艱澀難懂,也無法題提供各別圖元之x_y座標,因此筆者另編一個比較實用,可以適用在多種圖元(直線、多重線、矩形、橢圓()、圓()、正多邊形、星形、任意閉合多邊形等之邊碼供讀者參考。因為直線是多重線之特例,而矩形、橢圓()、圓()、正多邊形、星形均可視為多重線之延伸。因此只要編輯多重線之方法後,其餘只要參考或將各種圖元先轉換為多重線後直接援用多重線之方法即可。

 

(a)抽象基礎類別Drawable

Imports System.Math

Imports System.Xml.Serialization

 

Public MustInherit Class Drawable

 

    ' Drawing characteristics.

    Public ForeColor As Color

    Public FillColor As Color

    Public LineWidth As Integer = 0

    Public LineStyle As Integer = 0

    Public PenUse As Pen

    Public BrushUse As Brush

    Public IsSelected As Boolean = False

    ' Constructors.

    Public Sub New()

        ForeColor = Color.Black

        FillColor = Color.White

    End Sub

    Public Sub New(ByVal fore_color As Color, ByVal fill_color As Color, Optional ByVal line_width As Integer = 0, Optional ByVal line_style As Integer = 0, Optional ByVal isClosed As Boolean = False, Optional ByVal m_brushUse As Brush = Nothing, Optional ByVal isFillRgn As Boolean = False) 'only pass properties

        LineWidth = line_width

        ForeColor = fore_color

        FillColor = fill_color

        LineStyle = line_style

        BrushUse = m_brushUse

    End Sub

    ' Property procedures to serialize and

    ' deserialize ForeColor and FillColor.

    Public Property ForeColorArgb() As Integer

        Get

            Return ForeColor.ToArgb()

        End Get

        Set(ByVal Value As Integer)

            ForeColor = Color.FromArgb(Value)

        End Set

    End Property

 

    Public Property FillColorArgb() As Integer

        Get

            Return FillColor.ToArgb()

        End Get

        Set(ByVal Value As Integer)

            FillColor = Color.FromArgb(Value)

        End Set

    End Property

 

#Region "Methods to override"

    ' Draw the object on this Graphics surface.

    Public MustOverride Sub Draw(ByVal gr As Graphics)

    ' Return the object's bounding rectangle.

    Public MustOverride Function GetLimit() As Rectangle

    ' Return True if this point is on the object.

    Public MustOverride Function IsAt(ByVal ptTest As Point) As Boolean

    ' Return True if the object is empty (e.g. a zero-length line).

    Public MustOverride Function IsEmpty() As Boolean

#End Region

 

End Class

(b)衍生類別DrawableLine

Imports System.Math

Imports System.Drawing

Imports System.Drawing.Drawing2D

Imports System.Xml.Serialization

 

Public Class DrawablepolyLine

    Inherits Drawable

    ' Constructors.

    Public Sub New()

    End Sub

    Private m_Pts() As PointF, npt As Integer, m_Canvas As PictureBox, m_isClosed As Boolean, m_Pen As Pen, m_brushStyle As Integer, m_Brush As Brush

    Public Sub New(ByVal gr As Graphics, ByVal fore_color As Color, ByVal fill_color As Color, ByVal line_width As Integer, ByVal ptsIn() As PointF, _

                   Optional ByVal isClosed As Boolean = False, Optional ByVal brush_style As Integer = -1, Optional ByVal line_style As Integer = 0)

        MyBase.New(fore_color, fill_color, line_width, isClosed)

        Dim stGeoXyz As String = ""

        m_isClosed = isClosed

        m_brushStyle = brush_style

        If m_brushStyle = -1 Then m_Brush = New SolidBrush(fill_color)

        If m_brushStyle = 0 Then m_Brush = New HatchBrush(HatchStyle.Cross, fore_color)

        npt = UBound(ptsIn)

        If npt = 0 Then glPolyline.Pts(0) = ptsIn(0)

        If npt = 0 Then glPolyline.Pts(1) = ptsIn(0)

        For i As Integer = 0 To npt

            ReDim Preserve m_Pts(i), glPolyline.Pts(i)

            m_Pts(i) = ptsIn(i)

            glPolyline.Pts(i) = ptsIn(i)

        Next i

        LineLimit = getLimitLine(m_Pts)

        Draw(gr)

        '圖元資料以Collection集合儲存

        Dim stGeoProp As String

        stGeoProp = Trim("polyline" & "," & ColorTranslator.ToWin32(fore_color) & "," & ColorTranslator.ToWin32(fore_color) & "," & line_width & "," & line_style)

        collsGeoProp.Add(stGeoProp)

        stGeoXyz = Trim("polyline" & stGeoXyz)

        CollsGeoXyz.Add(stGeoXyz)

    End Sub

  

    Public Sub New(ByVal canvasIn As PictureBox, ByVal fore_color As Color, ByVal fill_color As Color, ByVal line_width As Integer, ByVal ptsIn() As PointF, Optional ByVal isClosed As Boolean = False)

        m_isClosed = isclosed

        npt = UBound(ptsIn)

        If npt = 0 Then glPolyline.Pts(0) = ptsIn(0)

        If npt = 0 Then glPolyline.Pts(1) = ptsIn(0)

        For i As Integer = 0 To npt

            ReDim Preserve m_Pts(i)

            m_Pts(i) = ptsIn(i)

        Next i

        m_Canvas = canvasIn

        If m_isClosed = True Then

            Dim highlight_brush As Brush = New SolidBrush(FillColor)

            m_Canvas.CreateGraphics.FillPolygon(highlight_brush, m_Pts)

        Else

            m_Canvas.CreateGraphics.DrawLines(New Pen(fore_color, line_width), m_Pts)

        End If

    End Sub  

    ' Draw the object on this Graphics surface.

    Public Overrides Sub Draw(ByVal gr As Graphics)

        On Error Resume Next

        If m_isClosed = True Then

            'Dim highlight_brush As Brush = New SolidBrush(FillColor)

            gr.FillPolygon(m_Brush, m_Pts)

        Else

            Dim highlight_pen As New Pen(ForeColor, LineWidth)

            gr.DrawLines(highlight_pen, m_Pts)

        End If

       

    End Sub

    '------------------------------

    Public Overrides Function Getlimit() As System.Drawing.Rectangle

        Return New Rectangle( _

            CInt(Min(LineLimit.Pt0.X, LineLimit.Pt0.X)), _

            CInt(Min(LineLimit.Pt0.Y, LineLimit.Pt1.Y)), _

            CInt(Abs(LineLimit.Pt1.X - LineLimit.Pt0.X)), _

            CInt(Abs(LineLimit.Pt1.Y - LineLimit.Pt0.Y)))

    End Function

    ' Return True if this point is on the object.

    Public Overrides Function IsAt(ByVal PtIn As Point) As Boolean

        Return PointNearSegment(PtIn, New Point(CInt(LineLimit.Pt0.X), CInt(LineLimit.Pt0.Y)), New Point(CInt(LineLimit.Pt1.X), CInt(LineLimit.Pt1.Y)))

    End Function

    ' Move the second point.

    ' Return True if the object is empty (e.g. a zero-length line).

    Public Overrides Function IsEmpty() As Boolean

        Return Abs(LineLimit.Pt0.X - LineLimit.Pt1.X) <= 1 AndAlso Abs(LineLimit.Pt0.Y - LineLimit.Pt1.Y) <= 1

    End Function

End Class

(b)衍生類別DrawablePolyLine

 

Imports System.Math

Imports System.Drawing

Imports System.Drawing.Drawing2D

Imports System.Xml.Serialization

 

Public Class DrawablepolyLine

    Inherits Drawable

    ' Constructors.

    Public Sub New()

    End Sub

    Private m_Pts() As PointF, npt As Integer, m_Canvas As PictureBox, m_isClosed As Boolean, m_Pen As Pen, m_brushStyle As Integer, m_Brush As Brush

    Public Sub New(ByVal gr As Graphics, ByVal fore_color As Color, ByVal fill_color As Color, ByVal line_width As Integer, ByVal ptsIn() As PointF, _

                   Optional ByVal isClosed As Boolean = False, Optional ByVal brush_style As Integer = -1, Optional ByVal line_style As Integer = 0)

        MyBase.New(fore_color, fill_color, line_width, isClosed)

        Dim stGeoXyz As String = ""

        m_isClosed = isClosed

        m_brushStyle = brush_style

        If m_brushStyle = -1 Then m_Brush = New SolidBrush(fill_color)

        If m_brushStyle = 0 Then m_Brush = New HatchBrush(HatchStyle.Cross, fore_color)

        npt = UBound(ptsIn)

        If npt = 0 Then glPolyline.Pts(0) = ptsIn(0)

        If npt = 0 Then glPolyline.Pts(1) = ptsIn(0)

        For i As Integer = 0 To npt

            ReDim Preserve m_Pts(i), glPolyline.Pts(i)

            m_Pts(i) = ptsIn(i)

            glPolyline.Pts(i) = ptsIn(i)

        Next i

        LineLimit = getLimitLine(m_Pts)

        '畫永久圖元時使用

        Draw(gr)

        '圖元資料以Collection集合儲存

        Dim stGeoProp As String

        stGeoProp = Trim("polyline" & "," & ColorTranslator.ToWin32(fore_color) & "," & ColorTranslator.ToWin32(fore_color) & "," & line_width & "," & line_style)

        collsGeoProp.Add(stGeoProp)

        stGeoXyz = Trim("polyline" & stGeoXyz)

        CollsGeoXyz.Add(stGeoXyz)

    End Sub

  

    Public Sub New(ByVal canvasIn As PictureBox, ByVal fore_color As Color, ByVal fill_color As Color, ByVal line_width As Integer, ByVal ptsIn() As PointF, Optional ByVal isClosed As Boolean = False)

        m_isClosed = isclosed

        npt = UBound(ptsIn)

        If npt = 0 Then glPolyline.Pts(0) = ptsIn(0)

        If npt = 0 Then glPolyline.Pts(1) = ptsIn(0)

        For i As Integer = 0 To npt

            ReDim Preserve m_Pts(i)

            m_Pts(i) = ptsIn(i)

        Next i

        '畫臨時性元時使用(如橡皮筋線)

        m_Canvas = canvasIn

        If m_isClosed = True Then

            Dim highlight_brush As Brush = New SolidBrush(FillColor)

            m_Canvas.CreateGraphics.FillPolygon(highlight_brush, m_Pts)

        Else

            m_Canvas.CreateGraphics.DrawLines(New Pen(fore_color, line_width), m_Pts)

        End If

    End Sub

  

    ' Draw the object on this Graphics surface.

    Public Overrides Sub Draw(ByVal gr As Graphics)

        On Error Resume Next

        If m_isClosed = True Then

            'Dim highlight_brush As Brush = New SolidBrush(FillColor)

            gr.FillPolygon(m_Brush, m_Pts)

        Else

            Dim highlight_pen As New Pen(ForeColor, LineWidth)

            gr.DrawLines(highlight_pen, m_Pts)

        End If

       

    End Sub

    '------------------------------

    Public Overrides Function Getlimit() As System.Drawing.Rectangle

 

        Return New Rectangle( _

            CInt(Min(LineLimit.Pt0.X, LineLimit.Pt0.X)), _

            CInt(Min(LineLimit.Pt0.Y, LineLimit.Pt1.Y)), _

            CInt(Abs(LineLimit.Pt1.X - LineLimit.Pt0.X)), _

            CInt(Abs(LineLimit.Pt1.Y - LineLimit.Pt0.Y)))

    End Function

 

    ' Return True if this point is on the object.

    Public Overrides Function IsAt(ByVal PtIn As Point) As Boolean

        Return PointNearSegment(PtIn, New Point(CInt(LineLimit.Pt0.X), CInt(LineLimit.Pt0.Y)), New Point(CInt(LineLimit.Pt1.X), CInt(LineLimit.Pt1.Y)))

    End Function

    ' Move the second point.

    ' Return True if the object is empty (e.g. a zero-length line).

    Public Overrides Function IsEmpty() As Boolean

        Return Abs(LineLimit.Pt0.X - LineLimit.Pt1.X) <= 1 AndAlso Abs(LineLimit.Pt0.Y - LineLimit.Pt1.Y) <= 1

    End Function

End Class

(c)呼叫程式碼

Dim m_NewDrawable As Drawable

Dim myBufferBitmap As Bitmap

Dim myBufferGraphics As Graphics

m_NewDrawable = New DrawablepolyLine(PicDraw, Color.White, Color.Red, 5, ptsIn)

  m_NewDrawable = New DrawablepolyLine(myBufferGraphics , Color.Black, Color.Red, 5, ptsIn)

m_NewDrawable = New DrawablepolyLine(myBufferGraphics, Color.Red, Color.Red, 5, ptsIn, True, _ 0)

細化及抽離作業應該適度,過或不及都不好,過度細化會讓程式混亂不易瞭解及維護;相反過度抽離會讓程式的基礎類別龐大容易造成錯誤。

19.3.2「是一種」與「有一個」的觀念

「細化」及「抽離」為類別繼承關係的重要觀念及技巧,「是一種(Is_a)」與「有一個(Has_a」的觀念,則能幫助User瞭解「細化」及「抽離」關係。Is_a是意指物件是某類別的特殊形式。舉例而言,人類(是動物一種,屬衍生類別)是哺乳動物(基礎類型)「是一種(Is_a)」特別類型,所以人類是哺乳動物(基礎類型)的衍生類別,這是Is_a的關係;Has_a關係則為某類別擁有某些項目作為屬性。如一般公司僱員多擁「有一個(Has_a」姓名、性別、年齡、教育程度、地址、薪俸等屬性,經理人則是僱員中的成員之一,因此僱員屬性是從經理人中抽離後的共同交集屬性,這是Has_a的關係。

19.3.3覆寫及遮蓋

在衍生類別增加類別的屬性、方法及事件是一種很容易的事情,您只要在衍生類別宣告就好。在基礎類別clsBase中有Public Function Add(),衍生類別clsDerived中也有Public Function Add(),二次衍生類別clsMorederivedPublic Function Add()。基礎類別以關鍵字Overridable修飾,衍生類別及二次衍生類別均以關鍵字Overrides修飾。在主程式呼叫引用方法Add()後,基礎類別bas1.add(1,2)=3,衍生類別被覆寫後變成減法,bas2.add(1,2)=-1,二次衍生類別被覆寫後變成乘法,bas3.add(1,2)=2

。注意,所有可以被覆寫及覆寫的方法、屬性、事件的存取範圍宣告均必須為Public

 

(a)類別程式碼

    Public Class clsBase

        Public Overridable Function Add(ByVal a As Double, ByVal b As Double) As Double

            Return a + b  '加法

        End Function

    End Class

 

    Public Class clsDerived

        Inherits clsBase

        Public Overrides Function Add(ByVal a As Double, ByVal b As Double) As Double

            '請將Private Shadows改為Public Shadows重新測試看看結果

            Return a - b   '減法

        End Function

    End Class

 

    Public Class clsMorederived

        Inherits clsDerived

        Public Overrides Function Add(ByVal a As Double, ByVal b As Double) As Double

            '請將Private Shadows改為Public Shadows重新測試看看結果

            Return a * b   '乘法

        End Function

    End Class

 

(b)主程式程式碼

        Dim bas1 As New clsBase

        Dim bas2 As New clsDerived

        Dim bas3 As New clsMorederived

        Dim retval1 As Double = bas1.Add(1, 2)

        ListBox1.Items.Add("bas1.add(1,2)= " & retval1)

        Dim retval2 As Double = bas2.Add(1, 2)

        ListBox1.Items.Add("bas2.add(1,2)= " & retval2)

        Dim retval3 As Double = bas3.Add(1, 2)

        ListBox1.Items.Add("bas3.add(1,2)= " & retval3)

 

如下面的程式碼,基礎類別Base中有公有函數Add(),衍生類別Derived中也有公有函數Add(),二次衍生類別Morederived也有公有函數Add()。如在主程式中引用Class BaseDerivedMorederived後所得答案,因衍生類別DerivedMorederived中之Function Add均均以Private Shadows宣告,故均採用基礎類別的函數Add(1,2),故所有答案均為3,縱使有Shadows關鍵字亦不會被遮蓋。

 

(a)類別程式碼

Public Class Base

    Public Function Add(ByVal a As Double, ByVal b As Double) As Double

        Return a + b  '加法

    End Function

End Class

 

Public Class Derived

    Inherits Base

    Private Shadows Function Add(ByVal a As Double, ByVal b As Double) As Double

    請將Private Shadows改為PublicShadows重新測試看看結果

        Return a - b   '減法

    End Function

End Class

 

Public Class Morederived

    Inherits Derived

    Private Shadows Function Add(ByVal a As Double, ByVal b As Double) As Double

請將Private Shadows改為PublicShadows重新測試看看結果

        Return a * b   '乘法

    End Function

End Class

 

(b)主程式程式碼

Private Sub btnShadows1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnShadows1.Click

      

Dim bas1 As New Base

        Dim bas2 As New Derived

        Dim bas3 As New Morederived

        Dim retval1 As Double = bas1.Add(1, 2)

        ListBox1.Items.Add("bas1.add(1,2)= " & retval1)

        Dim retval2 As Double = bas2.Add(1, 2)

        ListBox1.Items.Add("bas2.add(1,2)= " & retval2)

        Dim retval3 As Double = bas3.Add(1, 2)

        ListBox1.Items.Add("bas3.add(1,2)= " & retval3)

    End Sub

 

如將衍生類別DerivedMorederived中之Function Add如改均以Public Shadows宣告,則物件Bas1,bas2,bas3均採用自己的函數方法不會被遮蓋,故答案分別為3(加法),-1(減法),2(乘法)

 

19.3.4介面繼承

實作(Implements)關鍵字表示本類別有提供實作為介面(Interface)。一個介面為定義規範實作類別必須提供的行為[屬性或()方法],但不必提供此等行為的實作。如下面的類別clsInterface中問候介面(Igreeting,習慣上介面多以大寫I開頭),定義了問候介面的方法Sub GoodMorning()Sub GoodEvening(ByVal CurrentDate As DateTime)。在問候介面Igreeting 前有關鍵字InterFace修飾。類別clsInterface中包夾兩個類別Class EnglishClass Chinese

 

(a)類別程式碼

Public Class clsInterface

    Interface Igreeting

        Sub GoodMorning() '不需要存取範圍的關鍵字

        Sub GoodEvening(ByVal CurrentDate As DateTime) '不需要存取範圍的關鍵字

    End Interface

 

    Public Class English

        Implements Igreeting

 

        Public Sub GoodMorning() Implements Igreeting.GoodMorning

            Form1.ListBox1.Items.Add("Good morning!")

        End Sub

 

        Public Sub GoodEvening(ByVal CurrentDate As DateTime) Implements Igreeting.GoodEvening

            Form1.ListBox1.Items.Add("Good evening! Now it is  :" & CurrentDate)

        End Sub

    End Class

 

    Public Class Chinese

        Implements Igreeting

 

        Public Sub GoodMorning() Implements Igreeting.GoodMorning

            Form1.ListBox1.Items.Add("現在時間為:" & Now())

            Form1.ListBox1.Items.Add("您好!早安")

 

        End Sub

 

        Public Sub GoodEvening(ByVal CurrentDate As DateTime) Implements Igreeting.GoodEvening

            Form1.ListBox1.Items.Add("您好!晚安 , 現在時間為: " & CurrentDate)

        End Sub

    End Class

 

(b)主程式程式碼

 

Dim Hello As New clsInterface.English  '建構介面類別clsInterfac的英文物件

        Dim Hi As New clsInterface.Chinese '建構介面類別clsInterfac的中文物件

 

        Hello.GoodMorning() '實作英文早安問候

        Hello.GoodEvening(Now()) '實作英文晚安問候

 

        Hi.GoodMorning() '實作中文早安問候

        Hi.GoodEvening(Now()) '實作中文晚安問候

 

類別Class English Class Chinese 下一列均有陳述句Implements Igreeting。在早安GoodMorning()及晚安GoodEvening()問候Sub方法後面均跟隨有關鍵字Implements及對應的介面所定義的Sub名稱。類別中類別Class English Class Chinese中均有可以實作的程式碼。

正因為實作介面只提供行為規範,並不提供實作內容,實用上沒有繼承方便,如非必要應儘量使用繼承,此因為繼承可以保有父類別的屬性及方法等。但VB.NET並不支援子類別同時繼承多個父類別,因此如類別需繼承多個父類別的屬性及方法時,就需要使用實作介面。

 

19.4多態或多型

多態或多型(Polymorphism)為類別可以同時使用不同個數的引數及資料型態,以定義多個相同名稱的屬性、方法、及事件。在同一Class中,可以使用相同名稱的方法(SubFunction),只要方法中成員所使用參數不同即可,此即為方法多載(Method overload)。如下面的類別Public Class ClsPhoneCall程式碼基礎類別Public Class ID中提供Public Sub New(ByVal Number As String)Public Overridable Sub Dial(),衍生類別Public Class Phone及二次衍生類別Public Class CreditCardID,均提供方法Public Sub New(ByVal Number As String)Public Overrides Sub Dial(),其中Dial()均有覆寫關鍵字Overrides,因此衍生類別及二次衍生類別物件引用Dial()方法都採用覆寫後的Dial()方法,同樣一個類別可以同時定義不同的Dial()方法,此即為類別的多態或多型。

 

(a)類別程式碼

Public Class ClsPhoneCall

   Public Class ID '基礎類別,身份類別

        Public Number As String

        Protected stTpt As String

        Public Sub New(ByVal Number As String'建構式

            Me.Number = Number '自己呼叫自己用Me

        End Sub

        Public Overridable Sub Dial()  '可以被覆寫

            stTpt = "撥號: " & Number

            Form6.ListBox1.Items.Add(stTpt)

        End Sub

    End Class

 

   Public Class Phone  '衍生類別,電話類別

        Inherits ID

        Public Sub New(ByVal Number As String'建構式

            MyBase.New(Number) '在衍生類別呼叫基礎類別中的方法,屬性,事件用MyBase

        End Sub

 

        Public Overrides Sub Dial()  '覆寫

            stTpt = "按鈕電話: " & Number

            Form6.ListBox1.Items.Add(stTpt)

        End Sub

    End Class

 

   Public Class CreditCardID '二次衍生類別,信用卡類型

        Inherits ID

        Public Sub New(ByVal Number As String'建構式

            MyBase.New(Number) '在衍生類別呼叫基礎類別中的方法,屬性,事件用MyBase

        End Sub

        Public Overrides Sub Dial() '覆寫

            stTpt = "撥號電號: " & Number

            Form6.ListBox1.Items.Add(stTpt)

        End Sub

    End Class

End Class

 

(b)主程式程式碼

Dim card As New ClsPhoneCall.CreditCardID("統一編號A1234-5678") '建構二次衍生類別物件

        Dim phone As New ClsPhoneCall.Phone("電話號碼02-1234-5678") '建構衍生類別物件

        ListBox1.Items.Clear()

        ListBox1.Items.Add("使用標準物件")

        card.Dial() '實作二次衍生類別Dial()方法

        phone.Dial() '實作衍生類別Dial()方法

 

        Dim PolyID As ClsPhoneCall.ID '設定基礎類別的物件

        ListBox1.Items.Add("使用多態電話物件")

        PolyID = card '重新設定基礎類別物件為二次衍生類別物件

        PolyID.Dial()

        PolyID = phone '重新設定基礎類別物件為衍生類別物件

    PolyID.Dial()

19.5建構式

建構式或建構子(Constructor)Class與外部溝通的對話窗口,在呼叫程式中以New 關鍵字與ClassNew程式陳述句呼應以執行程式的初始化工作。

 

(a)類別程式碼

Public Class ClsConstructor

    Public Class Point

        Private mX, mY As Double

 

        Public Sub New()'無引數之次程序

        End Sub

 

        Public Sub New(ByVal xValue As Double, _

           ByVal yValue As Double'有引數之次程序,定義點類別為配對倍精準浮點數

            MsgBox("X,Y= " & xValue & ";" & yValue)

            mX = xValue

            mY = yValue

        End Sub

 

        Public Overrides Function ToString() As String

            Return "(" & mX & ", " & mY & ")"

            MsgBox("in class Point: Overrides Function Point(" & mX.ToString() & ", " & mY.ToString() & ")")

        End Function ' 覆寫為字串

 

    End Class

 

    Public Class Circle

        Inherits Point '繼承點類此

        Private mRadius As Double

 

        Public Sub New()

        End Sub

 

        Public Sub New(ByVal xValue As Double, _

           ByVal yValue As Double, ByVal radValue As Double) '定義圓類別為配對倍精準浮點數

            MyBase.New(xValue, yValue) '繼承自點類別

            Form1.ListBox1.Items.Add("In class Circle: X,Y,R= " & xValue & ";" & yValue & ";" & radValue)

            mRadius = radValue

        End Sub ' New

 

        Public Overrides Function ToString() As String

            Return "圓心= " & MyBase.ToString() & _

               "; 半徑 = " & mRadius

            Form1.ListBox1.Items.Add("In class Circle: R= " & mRadius)

        End Function ' ToString

 

    End Class

 

End Class

 

下面程式碼(主程式)利用建構式與類別ClsConstructor對口

 

(b)主程式程式碼

Dim Point1, Point2 As ClsConstructor.Point

        Dim Circle1, Circle2 As ClsConstructor.Circle

        Point1 = New ClsConstructor.Point(50.0F, 50.0F)

        'ClsConstructor.Point()Sub New()對口

        Circle1 = New ClsConstructor.Circle(120.0, 89.0, 3.7)

        'ClsConstructor..Circle()Sub New()對口

        ListBox1.Items.Clear()

        ListBox1.Items.Add("1: " & point1.ToString() & _

           vbCrLf & "1: " & circle1.ToString())

        point2 = circle1

        ListBox1.Items.Add("1 (via 2): " & point2.ToString())

        Circle2 = CType(Point2, ClsConstructor.Circle) ' point2轉換為圓

        ListBox1.Items.Add("1 (via 2): " & circle2.ToString())

        If (TypeOf point1 Is ClsConstructor.Circle) Then

            Circle2 = CType(Point1, ClsConstructor.Circle) 'point1轉換為圓

            ListBox1.Items.Add("點參考到圓")

        Else

            ListBox1.Items.Add("點未參考到圓"

        End If

 

Name(您的大名)
E_MAIL(您的電子信箱)
Comment or Suggestion(您想反應的狀況,建議,或諮詢事項)
首頁


 

 

 

 

 

首頁 | 如何使用Excel試算表作程式資料輸入 | 如何繪製等高線 | 解3D隱函數 | 工程仲裁案例說明 | Spline_Bezier曲線測繪 | VB6工程計算機程式設計 | VB NET工程計算機程式設計 | 如何在VB6中使用Vbscript & Dll | 徐昇多邊形 | 物件導向程式簡介 | 如何在VB6使用VB.net圖案筆刷及顏色表 | VB Net Graphics method(B) | Graphic method in vb net(A)

上次修改此網站的日期: 2018年12月02日