Anhand eines einfachen Beispiels möchten wir Ihnen mit diesem Workshop die Bedeutung und Anwendung von Polymorphismus (Schnittstellenimplementierung) erklären. Der Begriff Polymorphismus wird i.a.R. in Zusammenhang mit dem Begriff OOP (Objekt-Orientierte-Programmierung) unter VB6 verwendet und bedeutet nichts anderes, als das Erstellen einer Klassen-Schnittstellen-Beschreibung und diese in beliebigen anderen Klassen-Modulen zu implementieren / zu verwenden. Haben Sie sich schon mal gefragt, warum in einer Prozedur, die ein "StdPicture"-Objekt erwartet, auch ein "Picture"-, ein "IPictureDisp"- oder "IPicture"-Objekt übergeben werden kann? Normalerweise quittiert VB uns solch ein Verhalten mit der Fehlermeldung "Typen unverträglich". Dies liegt daran, dass all diese Objekte sich von der "StdPicture"-Objektklasse ableiten. All die genannten Objekte haben die "StdPicture"-Schnittstelle implementiert. Eigentlich hätte ein Objekt dieses Typs sicherlich gereicht, der Grund für die vielen "fast" identischen Objekte war vermutlich die Namensgebung. Auf den ersten Blick scheint das kein gutes Beispiel für die Schnittstellenimplementierung zu sein, aber bedenken Sie folgendes. Auch wenn alle Objekte dieselbe Schnittstelle haben, kann der Code der Klassen grundverschieden sein. So kann es z.B. sein, dass ein Objekt die Bildinformationen im RAM-Speicher ablegt, ein anderes die Bildinformationen im Virtuellen Speicher zwischenlagert und ein drittes dies in einer temporären Datei tut. Auch wenn die Kompatibilität und die Schnittstelle dieselbe ist, der Code innerhalb der Klasse ist es wahrscheinlich nicht! Weitere Vorteile des Implementierens von Schnittstellen ist die einfache Erweiterung bereits fertiger Projekte. Stellen Sie sich vor, Sie haben ein Projekt entwickelt, bei dem es darum geht, spezielle Formen (Kreis, Quadrat, etc.) in ein Bildfeld zu zeichnen. Das Projekt ist fertig und alles wunderbar. Nun hat aber einer Ihrer Kunden den Wunsch ein "Dreieck" zu implementieren. Nun haben nicht nur Sie eine Menge Aufwand, bereits existierenden Code umzugestalten, sondern auch Ihre Kunden, um den veränderten Code zu integrieren. Schnittstellenimplementierung schützt vor diesem Mehraufwand. Sie brauchen nur eine weitere Klasse in Ihr Projekt einbinden, ohne bereits geschriebenen Code anpassen zu müssen. Für Kunden, die dieses "Dreieck" nicht benötigen, ändert sich auch nichts! Außerdem haben Sie den Vorteil der Übersichtlichkeit, da alle Routinen der verschiedenen Zeichenmethoden in einer separaten Klasse abgelegt sind. Vielleicht schreiben Sie ein paar Codezeilen mehr durch diese Technik, aber es wird sich durch Geschwindigkeit, Übersichtlichkeit und zufriedene Kunden bezahlt machen. Dies wird im folgenden Beispiel demonstriert. Wir schreiben eine Schnittstelle "Fläche", die alle Eigenschaften und Prozeduren enthält, die unsere weiteren Klassen benötigen um ein "Quadrat", einen "Kreis" und ein "Dreieck" zu zeichnen. Nach dem Erstellen des Projekts können Sie selbst auch noch Klassen hinzufügen, um die einfache Erweiterbarkeit zu testen. Starten Sie ein neues Projekt und fügen Sie ein Klassenmodul hinzu. Diesem Klassenmodul geben Sie den Namen "Flaeche". Diese Klasse füllen Sie mit folgendem Code um unsere Schnittstelle zu definieren: Option Explicit Public Property Get TypName() As String End Property Public Property Get Left() As Long End Property Public Property Let Left(ByVal NewVal As Long) End Property Public Property Get Right() As Long End Property Public Property Let Right(ByVal NewVal As Long) End Property Public Property Get Top() As Long End Property Public Property Let Top(ByVal NewVal As Long) End Property Public Property Get Bottom() As Long End Property Public Property Let Bottom(ByVal NewVal As Long) End Property Public Function Draw(ByVal hdc As Long, ByVal Color As Long) End Function Fügen Sie anschließend ein öffentliches Modul mit folgenden Deklarationen hinzu, die zum Zeichnen der Objekte benötigt werden: Option Explicit Public Declare Function CreateSolidBrush Lib "gdi32" ( _ ByVal crColor As Long) As Long Public Declare Function DeleteObject Lib "gdi32" ( _ ByVal hObject As Long) As Long Public Declare Function Ellipse Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal X1 As Long, ByVal Y1 As Long, _ ByVal X2 As Long, ByVal Y2 As Long) As Long Public Declare Function Rectangle Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal X1 As Long, ByVal Y1 As Long, _ ByVal X2 As Long, ByVal Y2 As Long) As Long Public Declare Function Polygon Lib "gdi32" ( _ ByVal hdc As Long, _ lpPoint As POINTAPI, _ ByVal nCount As Long) As Long Public Declare Function SelectObject Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal hObject As Long) As Long Public Type POINTAPI x As Long y As Long End Type Public Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type ' ermittelt ein quadratisches Rect innerhalb eines rechteckigen Rects Public Function AbsoulteRect(ByRef R As RECT) As RECT Dim TmpHeight As Long, TmpWidth As Long TmpHeight = R.Bottom - R.Top TmpWidth = R.Right - R.Left If TmpWidth > TmpHeight Then TmpWidth = TmpHeight ElseIf TmpHeight > TmpWidth Then TmpHeight = TmpWidth End If With AbsoulteRect .Left = R.Left + (((R.Right - R.Left) - TmpWidth) / 2) .Top = R.Top + (((R.Bottom - R.Top) - TmpHeight) / 2) .Right = TmpWidth + .Left .Bottom = TmpHeight + .Top End With End Function Erstellen von Klassen mit Schnittstellen-Erbung Nun schreiben wir unsere erste Klasse, die die von uns bereits geschriebene Schnittstelle implementiert. Fügen Sie ein neues Klassenmodul ("Dreieck") mit folgendem Code hinzu: Option Explicit ' Schnittstellen der Klasse "Flaeche" verwenden Implements Flaeche Private R As RECT Private Property Get Flaeche_TypName() As String Flaeche_TypName = "Dreieck" End Property Private Property Get Flaeche_Left() As Long Flaeche_Left = R.Left End Property Private Property Let Flaeche_Left(ByVal RHS As Long) R.Left = RHS End Property Private Property Get Flaeche_Top() As Long Flaeche_Top = R.Top End Property Private Property Let Flaeche_Top(ByVal RHS As Long) R.Top = RHS End Property Private Property Get Flaeche_Right() As Long Flaeche_Right = R.Right End Property Private Property Let Flaeche_Right(ByVal RHS As Long) R.Right = RHS End Property Private Property Get Flaeche_Bottom() As Long Flaeche_Bottom = R.Bottom End Property Private Property Let Flaeche_Bottom(ByVal RHS As Long) R.Bottom = RHS End Property Private Function Flaeche_Draw(ByVal hdc As Long, _ ByVal Color As Long) Dim hBrushOld As Long Dim Points(2) As POINTAPI Dim TmpR As RECT ' absolutes Rechteck ermitteln TmpR = AbsoulteRect(R) ' Punkte des Dreiecks definieren Points(0).x = (TmpR.Right - TmpR.Left) + TmpR.Left Points(0).y = TmpR.Top Points(1).x = TmpR.Left Points(1).y = TmpR.Bottom Points(2).x = TmpR.Right Points(2).y = TmpR.Bottom ' Dreieck zeichnen hBrushOld = SelectObject(hdc, CreateSolidBrush(Color)) Call Polygon(hdc, Points(0), 3) DeleteObject SelectObject(hdc, hBrushOld) End Function Nach dem Hinzufügen der Codezeile Implements Flaeche hat sich schon viel getan. In der linken Auswahlbox des Codefensters ist ein Objekt hinzugekommen. Wird dieses ausgewählt, können Sie über die rechte Auswahlbox alle Schnittstellen hinzufügen (einfaches Auswählen genügt). Dies muss auch für alle Schnittstellen der implementierten Klassen geschehen, da der Debugger sonst beim Versuch, das Projekt zu starten, eine Fehlermeldung ausgibt (Man merke: Alle Schnittstellen hinzufügen!). Was wir mit der Klasse Dreieck gemacht haben wiederholen wir jetzt für die Klassen Kreis und Quadrat. Die Klasse "Kreis" enthält Folgendes: Option Explicit ' Schnittstellen der Klasse "Flaeche" verwenden Implements Flaeche Private R As RECT Private Property Get Flaeche_TypName() As String Flaeche_TypName = "Kreis" End Property Private Property Get Flaeche_Left() As Long Flaeche_Left = R.Left End Property Private Property Let Flaeche_Left(ByVal RHS As Long) R.Left = RHS End Property Private Property Get Flaeche_Top() As Long Flaeche_Top = R.Top End Property Private Property Let Flaeche_Top(ByVal RHS As Long) R.Top = RHS End Property Private Property Get Flaeche_Right() As Long Flaeche_Right = R.Right End Property Private Property Let Flaeche_Right(ByVal RHS As Long) R.Right = RHS End Property Private Property Get Flaeche_Bottom() As Long Flaeche_Bottom = R.Bottom End Property Private Property Let Flaeche_Bottom(ByVal RHS As Long) R.Bottom = RHS End Property Private Function Flaeche_Draw(ByVal hdc As Long, _ ByVal Color As Long) Dim hBrushOld As Long Dim TmpR As RECT ' absolutes Rechteck ermitteln TmpR = AbsoulteRect(R) ' Kreis zeichnen hBrushOld = SelectObject(hdc, CreateSolidBrush(Color)) Call Ellipse(hdc, TmpR.Left, TmpR.Top, TmpR.Right, TmpR.Bottom) DeleteObject SelectObject(hdc, hBrushOld) End Function Die Klasse "Quadrat" wird mit folgendem Code gefüllt: Option Explicit ' Schnittstellen der Klasse "Flaeche" verwenden Implements Flaeche Private R As RECT Private Property Get Flaeche_TypName() As String Flaeche_TypName = "Quadrat" End Property Private Property Get Flaeche_Left() As Long Flaeche_Left = R.Left End Property Private Property Let Flaeche_Left(ByVal RHS As Long) R.Left = RHS End Property Private Property Get Flaeche_Top() As Long Flaeche_Top = R.Top End Property Private Property Let Flaeche_Top(ByVal RHS As Long) R.Top = RHS End Property Private Property Get Flaeche_Right() As Long Flaeche_Right = R.Right End Property Private Property Let Flaeche_Right(ByVal RHS As Long) R.Right = RHS End Property Private Property Get Flaeche_Bottom() As Long Flaeche_Bottom = R.Bottom End Property Private Property Let Flaeche_Bottom(ByVal RHS As Long) R.Bottom = RHS End Property Private Function Flaeche_Draw(ByVal hdc As Long, _ ByVal Color As Long) Dim hBrushOld As Long Dim TmpR As RECT ' absolutes Rechteck ermitteln TmpR = AbsoulteRect(R) ' Quadrat zeichnen hBrushOld = SelectObject(hdc, CreateSolidBrush(Color)) Call Rectangle(hdc, TmpR.Left, TmpR.Top, TmpR.Right, TmpR.Bottom) DeleteObject SelectObject(hdc, hBrushOld) End Function Anwendung - Aufruf der Klassen Abschließend benötigen wir noch den Code des Formulars. Auf dem Formular ordnen wir noch schnell ein Bildfeld ("Picture1", AutoRedraw = "TRUE", ScaleMode = "3 - Pixel") und eine ComboBox ("Combo1", Style = "DropDownListe") an. Wird der folgende Code hinzugefügt ist das Projekt fertig: Option Explicit Private Sub Form_Load() With Combo1 .AddItem "Dreieck" .AddItem "Kreis" .AddItem "Quadrat" .ListIndex = 0 End With End Sub ' ruft die Zeichenmethode mit einem neuem Objekt auf Private Sub Combo1_Click() Select Case Combo1.ListIndex Case 0 Call Zeichne(New Dreieck) Case 1 Call Zeichne(New Kreis) Case 2 Call Zeichne(New Quadrat) End Select End Sub ' zeichnet das Objekt Public Sub Zeichne(ByRef F As Flaeche) ' Bildfeld zurücksetzen (AutoReDraw = TRUE) Picture1.Cls ' Objekt mit einem Abstand von 10 Pixeln zum Rand zentriert zeichnen With F Debug.Print "Zeichne : " & .TypName .Left = 10 .Top = 10 .Right = Picture1.ScaleWidth - 10 .Bottom = Picture1.ScaleHeight - 10 .Draw Picture1.hdc, vbGreen End With End Sub Wenn Sie das Projekt nun starten werden Sie sehen, dass bei der Auswahl eines Eintrags der ComboBox das jeweilige Objekt gezeichnet wird. In der "Combo1_Click" Prozedur ist schön zu sehen, wie verschiedene Objekte mit der implementierten Schnittstelle der "Flaeche"-Klasse an ein und die selbe Prozedur weitergereicht werden. Die Objekte liefern verschiedene Ergebnisse, obwohl sie auf dieselbe Art und Weise angesprochen werden. Fügen Sie selbst noch eine Klasse hinzu um die leichte Erweiterbarkeit zu testen. Gruß Dieser Workshop wurde bereits 24.542 mal aufgerufen.
Anzeige
![]() ![]() ![]() Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
vb@rchiv CD Vol.6 ![]() ![]() Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! Online-Update-Funktion Entwickler-Vollversionen u.v.m. Tipp des Monats ![]() Dieter Otter PopUp-Menü wird nicht angezeigt :-( In diesem Tipp verraten wir Ihnen, wie Sie Probleme mit PopUp-Menüs umgehen können, wenn diese unter bestimmten Umständen einfach nicht angezeigt werden. sevWizard für VB5/6 ![]() Professionelle Assistenten im Handumdrehen Erstellen Sie eigene Assistenten (Wizards) im Look & Feel von Windows 2000/XP - mit allem Komfort und zwar in Windeseile :-) |
|||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |