改變思路,從另一個角度使用多態
髡咧饕常?a href="http://usevb.yeah.net/">傻兒VB園作者信箱:王文濤
作者在寫《傻瓜打表》部件時,原本想提供打印預覽功能,因當時眼界較窄,所知道的只有一個方法實現打印預覽: 定義一個 Object 變量(如:ObjPrn),用該變量替換程序中的與打印有關的語句中的 Printer 對象,打印時,首先設置
Set ObjPrn = Printer
預覽時設置
Set ObjPrn = frmPreV.Picture1
用上述方法雖然可以實現打印和預覽,但是,顯然對打印機和實現預覽的圖像控件的訪問都是后期邦定的,這在效率上是不高的。打算使用多態技術,可是 Printer 對象和 Picturebox控件不是從一個類(VB中可引用的)派生的。因無法實現打印和預覽的前期綁定,故沒有提供打印預覽功能。
一天,當再次閱讀VB幫助中的多態章節時,突然想到了一個方法,使用該方法,只要兩個類具有相同的部分接口(方法、屬性及參數),即便這兩個類毫不相干,也可以通過變相用多態技術提供對這兩個類的前期綁定。
多態是干什么的?
MSDN說:多態意味著許多類可以提供同樣的屬性或者方法,而且調用者在調用這些屬性或方法之前,不必知道某個對象屬于什么類。
多態的意義之一就是,通過多態接口,對多個不同類的訪問可以獲得高效的前期綁定。
一般來說,如果幾個類具有部分相同的屬性、方法,但卻不是用多態接口實現的(例如Printer和Form),將不能提供類似多態的性能。
但是,“軟件”正如其名字中的“軟”,是一個靈活的、可塑的東西,我們可以改變思維模式,換一個角度:沒有多態接口的兩個類(如Printer、Picturebox)如同兩個人不同父,不是兄弟;但我們可以讓其子同娶一家之女(另外具有多態接口的兩個類),其子為連襟,其兒媳為姐妹,而通過其兒媳訪問其本人具有相同的路徑。
還以上例,其中的關鍵是:添加三個類,分別Printer之妻、Picturebox之妻、二女之父(在二女中要實現的、Printer和Picturebox共有的部分接口)。下面就用打印和預覽為例,介紹該方法:
第一步、添加一個抽象類,在該類中定義共同的方法和屬性。
如果方法名稱是VB關鍵字,可以修改(如Line方法,本例中改為pLine方法);如果參數形式不符合VB語法,也可作相應修改(如本例中Line方法的參數,應為"(X1, -(X2,Y2),Forcolor,BF",顯然,在VB中的參數不能用這種形式;還有Print方法,打印參數之間、后面可以跟逗號、分號等,也要相應改變,并且不能提供全部原來Print方法功能) :
類名:IMyPrinter,為簡化,示例中僅包含作為示例的接口。
Public Property Get CurrentX() As Single
End Property
Public Property Let CurrentX(ByVal RHS As Single)
End Property
Public Sub EndDoc()
End Sub
Public Property Get Font() As stdole.Font
End Property
Public Property Set Font(ByVal RHS As stdole.Font)
End Property
Public Sub PLine(ByVal X1 As Single, ByVal Y1 As Single, ByVal X2 As Single, _
ByVal Y2 As Single, Optional ByVal HasB As Boolean = False, _
Optional ByVal HasF As Boolean = False)
End Sub
Public Function ScaleX(ByVal Width As Single, Optional ByVal FromScale As Variant, _
Optional ByVal ToScale As Variant) As Single
End Function
Public Function TextWidth(ByVal Str As String) As Single
End Function
Public Property Let Width(ByVal RHS As Long)
End Property
Public Property Get Width() As Long
End Property
Public Sub PPrint(Optional ByVal F0D1H2 As Integer = 0, Optional PrnInfo)
End Sub
第二步、添加兩個類,實現該接口,并分別提供Printer對象和Picturebox控件相應的功能。
下面僅以在Picturebox控件上實現該接口的類的部分代碼作為示例:還需要添加一個實現預覽的窗體(frmPreView),在上面添加一個Picturebox控件(pic)
類名:MyPic
Implements IMyPrinter
Dim frm As frmPreview
Private pic As PictureBox
Private Sub Class_Initialize()
Set frm = New frmPreview
Load frm
Set pic = frm.pic
End Sub
Private Sub Class_Terminate()
Set pic = Nothing
Unload frm
Set frm = Nothing
End Sub
Private Property Let IMyPrinter_CurrentX(ByVal RHS As Single)
pic.CurrentX = RHS
End Property
Private Property Get IMyPrinter_CurrentX() As Single
IMyPrinter_CurrentX = pic.CurrentX
End Property
Private Sub IMyPrinter_EndDoc()
frm.Show vbModal
End Sub
Private Property Get IMyPrinter_Font() As StdFont
Dim F As StdFont
Set F = New StdFont
With pic.Font
F.Size = .Size
F.Name = .Name
F.Size = .Size
F.Bold = .Bold
F.Italic = .Italic
F.Strikethrough = .Strikethrough
F.Underline = .Underline
F.Weight = .Weight
End With
Set IMyPrinter_Font = F
End Property
Private Property Set IMyPrinter_Font(ByVal RHS As StdFont)
With pic.Font
.Size = RHS.Size
.Name = RHS.Name
.Size = RHS.Size
.Bold = RHS.Bold
.Italic = RHS.Italic
.Strikethrough = RHS.Strikethrough
.Underline = RHS.Underline
.Weight = RHS.Weight
End With
End Property
Private Sub IMyPrinter_PLine(ByVal X1 As Single, ByVal Y1 As Single, ByVal X2 As Single, ByVal Y2 As Single, Optional ByVal HasB As Boolean = False, Optional ByVal HasF As Boolean = False)
If HasF Then "本例中沒有提供顏色選項
pic.Line (X1, Y1)-(X2, Y2), , BF
ElseIf HasB Then
pic.Line (X1, Y1)-(X2, Y2), , B
Else
pic.Line (X1, Y1)-(X2, Y2)
End If
End Sub
Private Sub IMyPrinter_PPrint(Optional ByVal F0D1H2 As Integer = 0, Optional PrnInfo As Variant)
Select Case F0D1H2 "該參數為0:跟分號;1:跟逗號;2:無符號
Case 0
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo;
End If
Case 1
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo,
End If
Case 2
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo
Else
pic.Print
End If
End Select
End Sub
Private Function IMyPrinter_ScaleX(ByVal Width As Single, Optional ByVal FromScale As Variant, Optional ByVal ToScale As Variant) As Single
IMyPrinter_ScaleX = pic.ScaleX(Width, FromScale, ToScale)
End Function
Private Function IMyPrinter_TextWidth(ByVal Str As String) As Single
IMyPrinter_TextWidth = pic.TextWidth(Str)
End Function
Private Property Get IMyPrinter_Width() As Long
IMyPrinter_Width = pic.Width
End Property
Private Property Let IMyPrinter_Width(ByVal RHS As Long)
pic.Width = RHS
End Property
第三步、在主類模塊定義如下變量
Private cIMyPrn As IMyPrinter
Private cPic As MyPic
Private cPrn As MyPrn
在主類模塊Class_Initialize事件中,添加:
Set cPrn As New MyPrn
Set cIMyPrn = cPrn
在主類模塊中,將原來有關打印語句中的Printer替換為cIMyPrn。在實現預覽的方法中,添加:
Set cPic = New MyPic
Set cIMyPrn = cMyPic
修改特殊類型的語句(如Line、Print,方法名和參數形式已經改變,將其改為符合cIMyPrn語法的語句)
預覽結束后:
Set cIMyPrn = cPrn
Set cPic = Nothing
通過上面的方法,就可以用一個IMyPrinter變量,實現打印和預覽的前期綁定當然,該方法未必是實現打印、預覽的首選,還可以使用API方法,直接操縱設備場景來實現打印預覽,但該方法的意義不在于此,其意義在于該方法拓展了VB中提供的多態技術的應用范圍。
本文副標題中的所謂“高層次”不是作者層次高,而是編程方法和思路,不是面向底層、面向Win核心,而是在對象的高層、在編程語言和方法的較高層上實現多態。
作者在寫《傻瓜打表》部件時,原本想提供打印預覽功能,因當時眼界較窄,所知道的只有一個方法實現打印預覽: 定義一個 Object 變量(如:ObjPrn),用該變量替換程序中的與打印有關的語句中的 Printer 對象,打印時,首先設置
Set ObjPrn = Printer
預覽時設置
Set ObjPrn = frmPreV.Picture1
用上述方法雖然可以實現打印和預覽,但是,顯然對打印機和實現預覽的圖像控件的訪問都是后期邦定的,這在效率上是不高的。打算使用多態技術,可是 Printer 對象和 Picturebox控件不是從一個類(VB中可引用的)派生的。因無法實現打印和預覽的前期綁定,故沒有提供打印預覽功能。
一天,當再次閱讀VB幫助中的多態章節時,突然想到了一個方法,使用該方法,只要兩個類具有相同的部分接口(方法、屬性及參數),即便這兩個類毫不相干,也可以通過變相用多態技術提供對這兩個類的前期綁定。
多態是干什么的?
MSDN說:多態意味著許多類可以提供同樣的屬性或者方法,而且調用者在調用這些屬性或方法之前,不必知道某個對象屬于什么類。
多態的意義之一就是,通過多態接口,對多個不同類的訪問可以獲得高效的前期綁定。
一般來說,如果幾個類具有部分相同的屬性、方法,但卻不是用多態接口實現的(例如Printer和Form),將不能提供類似多態的性能。
但是,“軟件”正如其名字中的“軟”,是一個靈活的、可塑的東西,我們可以改變思維模式,換一個角度:沒有多態接口的兩個類(如Printer、Picturebox)如同兩個人不同父,不是兄弟;但我們可以讓其子同娶一家之女(另外具有多態接口的兩個類),其子為連襟,其兒媳為姐妹,而通過其兒媳訪問其本人具有相同的路徑。
還以上例,其中的關鍵是:添加三個類,分別Printer之妻、Picturebox之妻、二女之父(在二女中要實現的、Printer和Picturebox共有的部分接口)。下面就用打印和預覽為例,介紹該方法:
第一步、添加一個抽象類,在該類中定義共同的方法和屬性。
如果方法名稱是VB關鍵字,可以修改(如Line方法,本例中改為pLine方法);如果參數形式不符合VB語法,也可作相應修改(如本例中Line方法的參數,應為"(X1, -(X2,Y2),Forcolor,BF",顯然,在VB中的參數不能用這種形式;還有Print方法,打印參數之間、后面可以跟逗號、分號等,也要相應改變,并且不能提供全部原來Print方法功能) :
類名:IMyPrinter,為簡化,示例中僅包含作為示例的接口。
Public Property Get CurrentX() As Single
End Property
Public Property Let CurrentX(ByVal RHS As Single)
End Property
Public Sub EndDoc()
End Sub
Public Property Get Font() As stdole.Font
End Property
Public Property Set Font(ByVal RHS As stdole.Font)
End Property
Public Sub PLine(ByVal X1 As Single, ByVal Y1 As Single, ByVal X2 As Single, _
ByVal Y2 As Single, Optional ByVal HasB As Boolean = False, _
Optional ByVal HasF As Boolean = False)
End Sub
Public Function ScaleX(ByVal Width As Single, Optional ByVal FromScale As Variant, _
Optional ByVal ToScale As Variant) As Single
End Function
Public Function TextWidth(ByVal Str As String) As Single
End Function
Public Property Let Width(ByVal RHS As Long)
End Property
Public Property Get Width() As Long
End Property
Public Sub PPrint(Optional ByVal F0D1H2 As Integer = 0, Optional PrnInfo)
End Sub
第二步、添加兩個類,實現該接口,并分別提供Printer對象和Picturebox控件相應的功能。
下面僅以在Picturebox控件上實現該接口的類的部分代碼作為示例:還需要添加一個實現預覽的窗體(frmPreView),在上面添加一個Picturebox控件(pic)
類名:MyPic
Implements IMyPrinter
Dim frm As frmPreview
Private pic As PictureBox
Private Sub Class_Initialize()
Set frm = New frmPreview
Load frm
Set pic = frm.pic
End Sub
Private Sub Class_Terminate()
Set pic = Nothing
Unload frm
Set frm = Nothing
End Sub
Private Property Let IMyPrinter_CurrentX(ByVal RHS As Single)
pic.CurrentX = RHS
End Property
Private Property Get IMyPrinter_CurrentX() As Single
IMyPrinter_CurrentX = pic.CurrentX
End Property
Private Sub IMyPrinter_EndDoc()
frm.Show vbModal
End Sub
Private Property Get IMyPrinter_Font() As StdFont
Dim F As StdFont
Set F = New StdFont
With pic.Font
F.Size = .Size
F.Name = .Name
F.Size = .Size
F.Bold = .Bold
F.Italic = .Italic
F.Strikethrough = .Strikethrough
F.Underline = .Underline
F.Weight = .Weight
End With
Set IMyPrinter_Font = F
End Property
Private Property Set IMyPrinter_Font(ByVal RHS As StdFont)
With pic.Font
.Size = RHS.Size
.Name = RHS.Name
.Size = RHS.Size
.Bold = RHS.Bold
.Italic = RHS.Italic
.Strikethrough = RHS.Strikethrough
.Underline = RHS.Underline
.Weight = RHS.Weight
End With
End Property
Private Sub IMyPrinter_PLine(ByVal X1 As Single, ByVal Y1 As Single, ByVal X2 As Single, ByVal Y2 As Single, Optional ByVal HasB As Boolean = False, Optional ByVal HasF As Boolean = False)
If HasF Then "本例中沒有提供顏色選項
pic.Line (X1, Y1)-(X2, Y2), , BF
ElseIf HasB Then
pic.Line (X1, Y1)-(X2, Y2), , B
Else
pic.Line (X1, Y1)-(X2, Y2)
End If
End Sub
Private Sub IMyPrinter_PPrint(Optional ByVal F0D1H2 As Integer = 0, Optional PrnInfo As Variant)
Select Case F0D1H2 "該參數為0:跟分號;1:跟逗號;2:無符號
Case 0
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo;
End If
Case 1
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo,
End If
Case 2
If Not IsMissing(PrnInfo) Then
pic.Print PrnInfo
Else
pic.Print
End If
End Select
End Sub
Private Function IMyPrinter_ScaleX(ByVal Width As Single, Optional ByVal FromScale As Variant, Optional ByVal ToScale As Variant) As Single
IMyPrinter_ScaleX = pic.ScaleX(Width, FromScale, ToScale)
End Function
Private Function IMyPrinter_TextWidth(ByVal Str As String) As Single
IMyPrinter_TextWidth = pic.TextWidth(Str)
End Function
Private Property Get IMyPrinter_Width() As Long
IMyPrinter_Width = pic.Width
End Property
Private Property Let IMyPrinter_Width(ByVal RHS As Long)
pic.Width = RHS
End Property
第三步、在主類模塊定義如下變量
Private cIMyPrn As IMyPrinter
Private cPic As MyPic
Private cPrn As MyPrn
在主類模塊Class_Initialize事件中,添加:
Set cPrn As New MyPrn
Set cIMyPrn = cPrn
在主類模塊中,將原來有關打印語句中的Printer替換為cIMyPrn。在實現預覽的方法中,添加:
Set cPic = New MyPic
Set cIMyPrn = cMyPic
修改特殊類型的語句(如Line、Print,方法名和參數形式已經改變,將其改為符合cIMyPrn語法的語句)
預覽結束后:
Set cIMyPrn = cPrn
Set cPic = Nothing
通過上面的方法,就可以用一個IMyPrinter變量,實現打印和預覽的前期綁定當然,該方法未必是實現打印、預覽的首選,還可以使用API方法,直接操縱設備場景來實現打印預覽,但該方法的意義不在于此,其意義在于該方法拓展了VB中提供的多態技術的應用范圍。
本文副標題中的所謂“高層次”不是作者層次高,而是編程方法和思路,不是面向底層、面向Win核心,而是在對象的高層、在編程語言和方法的較高層上實現多態。