vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Brandneu! sevEingabe v3.0 - Das Eingabecontrol der Superlative!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: OOP / Tools   |   VB-Versionen: VB5, VB606.04.04
Polymorphismus (Schnittstellenimplementierung)

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

Autor:  Matthias VolkBewertung:     [ Jetzt bewerten ]Views:  24.779 

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ß
LonelySuicide666
 

Dieser Workshop wurde bereits 24.779 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6

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.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
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.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel