VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)
首页 - 经验之谈 - 改变思路,从另一个角度使用多态
发表评论(0)作者:不详, 平台:VB6.0+Win98, 阅读:8568, 日期:2001-06-18
改变思路,从另一个角度使用多态
王文涛
--------------------------------------------------------------------------------

b>摘要:
  作者在写《傻瓜打表》部件时,原本想提供打印预览功能,因当时眼界较窄,所知道的只有一个方法实现打印预览。打算使用多态技术,使用该方法,只要两个类具有相同的部分接口(方法、属性及参数),即便这两个类毫不相干,也可以通过变相用多态技术提供对这两个类的前期绑定。

  作者在写《傻瓜打表》部件时,原本想提供打印预览功能,因当时眼界较窄,所知道的只有一个方法实现打印预览: 定义一个 Object 变量(如:ObjPrn),用该变量替换程序中的与打印有关的语句中的 Printer 对象,打印时,首先设置

Set ObjPrn = Printer

预览时设置

Set ObjPrn = frmPreV.Picture1

  用上述方法虽然可以实现打印和预览,但是,显然对打印机和实现预览的图像控件的访问都是后期邦定的,这在效率上是不高的。打算使用多态技术,可是 Printer 对象和 Picturebox控件不是从一个类(VB中可引用的)派生的。因无法实现打印和预览的前期绑定,故没有提供打印预览功能。

  一天,当再次阅读VB帮助中的多态章节时,突然想到了一个方法,使用该方法,只要两个类具有相同的部分接口(方法、属性及参数),即便这两个类毫不相干,也可以通过变相用多态技术提供对这两个类的前期绑定。
多态是干什么的?

  MSDN说:多态意味着许多类可以提供同样的属性或者方法,而且调用者在调用这些属性或方法之前,不必知道某个对象属于什么类。
多态的意义之一就是,通过多态接口,对多个不同类的访问可以获得高效的前期绑定。

  一般来说,如果几个类具有部分相同的属性、方法,但却不是用多态接口实现的(例如Printer和Form),将不能提供类似多态的性能。
但是,“软件”正如其名字中的“软”,是一个灵活的、可塑的东西,我们可以改变思维模式,换一个角度:没有多态接口的两个类(如Printer、Picturebox)如同两个人不同父,不是兄弟;但我们可以让其子同娶一家之女(另外具有多态接口的两个类),其子为连襟,其儿媳为姐妹,而通过其儿媳访问其本人具有相同的路径。

  还以上例,其中的关键是:添加三个类,分别Printer之妻、Picturebox之妻、二女之父(在二女中要实现的、Printer和Picturebox共有的部分接口)。下面就用打印和预览为例,介绍该方法:

第一步、添加一个抽象类,在该类中定义共同的方法和属 ?/b>

  如果方法名称是VB关键字,可以修改(如Line方法,本例中改为pLine方法);如果参数形式不符合VB语法,也可作相应修改(如本例中Line方法的参数,应为"(X1,Y1)-(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核心,而是在对象的高层、在编程语言和方法的较高层上实现多态。