Schnittstellenvererbung

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Access-Entwickler sind nicht gerade verwöhnt, wenn es um objektorientierte Techniken unter VBA geht. Es gibt zwar die Möglichkeit, Klassen zu programmieren und Objekte auf Basis dieser Klassen zu erzeugen, aber Features wie Vererbung fallen weitgehend flach. Weitgehend Nun, zumindest die sogenannte Schnittstellenvererbung ist unter VBA möglich. Damit können Sie immerhin mehrere Ausprägungen einer Klasse nach den Vorgaben einer Schnittstelle implementieren und diese je nach Bedarf austauschen.

Bevor wir uns den technischen Feinheiten widmen, schauen wir uns erstmal den Sinn und Zweck der Schnittstellenvererbung an. Es gibt Anforderungen, die sehr ähnliche Vorgänge mit verschiedenen Techniken umsetzen. Ein Beispiel dafür ist das Versenden von E-Mails: Sie können dazu Outlook fernsteuern oder alternativ eine der vielen Klassen zum direkten Versenden von E-Mails via Winsock und SMTP einsetzen. Für Ihre Anwendung ist das Ziel immer gleich: Die E-Mail soll beim Empfänger ankommen und dafür stellen wir ein Paket aus Informationen wie Empfänger, Absender, Betreff und Inhalt der E-Mail zusammen. Der Rest interessiert uns nicht – dies sollen entsprechende Prozeduren erledigen.

Ein ähnliches Beispiel ist die Ausgabe von Daten. Diese sollen einmal in einem Bericht landen, ein anderes Mal in einem Word-Dokument, vielleicht aber auch einfach in einer Textdatei, in einem HTML- oder XML-Dokument oder als schlichte Ausgabe im Direktfenster des VBA-Editors.

Auch hier haben wir wenig Lust, uns darum zu kümmern, wie der Inhalt im entsprechenden Ausgabeformat landet – wir möchten höchstens festlegen, welches Ausgabeformat verwendet wird und gegebenenfalls die dorthin geschickten Daten filtern oder sortieren.

Dies alles ist mit herkömmlichen Prozeduren möglich. Im Falle der E-Mails würden Sie die Daten wie Absender, Empfänger, Betreff und Text etwa per Formular abfragen und dann die Prozedur zum Versenden der E-Mail aufrufen.

Diese entscheidet dann auf Basis vorher festgelegter Kriterien, ob die E-Mail mit Outlook oder direkt per SMTP verschickt wird, und steuert den entsprechenden Zweig einer If…Then– oder Select Case-Bedingung an. In den verschiedenen Zweigen steckt dann versandspezifischer Code oder schlicht ein Aufruf einer Funktion, welche den Versand auf die angegebene Weise durchführt.

Sie würden dann beispielsweise zwei Funktionen definieren, deren Prozedurköpfe etwa so aussehen:

Public Function MailToOutlook(strSender As String, strRecipient As String, strSubject As String, strBody As String)
    Public Function MailToSMTP(strSender As String, strRecipient As String, strSubject As String, strBody As String)

Die aufrufende Prozedur enthielte dann beispielsweise den folgenden Code:

Select Case strVersandart
    Case "Outlook"
        Call MailToOutlook (...)
    Case "SMTP"
        Call MailToSMTP (...)
End Select

Sie haben hier alle Freiheiten: Die Funktionen MailToOutlook oder MailToSMTP könnten beispielsweise auch zusätzliche Parameter verwenden, oder Sie erweitern das Select Case-Statement um eine dritte Methode zum Versenden von E-Mails.

Klasse Funktion

Die Funktionen zum Versenden von E-Mails könnten auch in zwei Klassen ausgelagert werden. Die Klasse zum Versenden von E-Mails via SMTP könnte beispielsweise wie in Listing 1 aussehen. Die Klasse für den Versand mit Outlook sieht genauso aus, nur dass die Methode zum Versenden der E-Mail den Namen SendSMTPMail besitzt.

Listing 1: Klasse zum Versenden von E-Mails via SMTP

Private m_Sender As String
Private m_Recipient As String
Private m_Subject As String
Private m_Body As String
Public Property Let Sender(strSender As String)
    m_Sender = strSender
End Property
Public Property Let Recipient(strRecipient As String)
    m_Recipient = strRecipient
End Property
Public Property Let Subject(strSubject As String)
    m_Subject = strSubject
End Property
Public Property Let Body(strBody As String)
    m_Body = strBody
End Property
Public Function SendSMTPMail() As Boolean
    ''... Code zum Senden der Mail per SMTP
    SendSMTPMail = True
End Function

Eine Prozedur, welche eine dieser beiden Klassen aufruft, sieht etwa wie in Listing 2 aus. Sie bekommt per Parameter den Versender, den Empfänger, den Betreff und den Inhalt sowie die Versandart übermittelt. Mit diesen Informationen entscheidet die Select Case-Bedingung, welcher der beiden Zweige angesteuert werden soll.

Listing 2: Instanzierung der Klassen zum Versenden von E-Mails und Versenden der E-Mail

Public Sub MailVersenden(strAbsender As String, strEmpfaenger As String, strBetreff As String, strInhalt As String, strVersandart As String)
    Dim objSendSMTPMail As clsSendSMTPMail
    Dim objSendOutlookMail As clsSendOutlookMail
    Select Case strVersandart
        Case "SMTP"
            Set objSendSMTPMail = New clsSendSMTPMail
            With objSendSMTPMail
                .Sender = strAbsender
                .Recipient = strEmpfaenger
                .Subject = strBetreff
                .Body = strInhalt
                If .SendSMTPMail = True Then
                    MsgBox "Versand via SMTP erfolgreich!"
                End If
            End With
        Case "Outlook"
            Set objSendOutlookMail = New clsSendOutlookMail
            With objSendOutlookMail
                .Sender = strAbsender
                .Recipient = strEmpfaenger
                .Subject = strBetreff
                .Body = strInhalt
                If .SendOutlookMail = True Then
                    MsgBox "Versand via Outlook erfolgreich!"
                End If
            End With
    End Select
End Sub

Dadurch wird eine der beiden Klassen clsSendSMTPMail und clsSendOutlookMail instanziert. Danach füllt die Prozedur die Eigenschaften des auf diese Weise erzeugten Objekts und initiiert den Mailversand durch den Aufruf der Methode SendSMTPMail beziehungsweise SendOutlookMail.

Enge Kopplung

Die aufrufende Klasse ist dann allerdings sehr eng an die aufgerufenen Klassen gekoppelt. Wenn Sie die aufgerufenen Klassen so gestalten können, dass diese alle eine einheitliche Schnittstelle bieten – das heißt, dass jede Klasse die gleichen Methoden und Eigenschaften enthält -, können Sie eine abstrakte Schnittstelle für diese Klassen erstellen. Diese Schnittstelle fungiert dabei als eine Art “Vertrag” – sie legt genau fest, wie die Implementierung der Klasse aussehen muss.

Klasse als Schnittstelle

Diese Schnittstelle ist eine eigene Klasse, die alle enthaltenen Methoden und Eigenschaften festlegt, aber keine Implementierungsdetails enthält.

Sie sagt also beispielsweise: Die Klasse, die meine Schnittstelle implementiert, muss vier öffentliche Property Let-Parameter namens strSender, strRecipient, strSubject und strBody mit dem Datentyp String und eine öffentliche Sub-Methode namens Send aufweisen – nicht mehr und nicht weniger.

Die Implementierung dieser Methoden und Eigenschaften folgt erst in den eigentlichen Klassen.

Die Implementierung enthält die eigentliche Funktionalität. Um sicherzustellen, dass die in der Schnittstelle vorgegebene Definition eingehalten wird, enthält diese Klasse die Anweisung Implements ISendMail. Die implementierten Methoden und Eigenschaften weisen eine spezielle Notation auf: Diese besteht aus dem Namen der zu implementierenden Schnittstelle, einem Unterstrich (_) und dem in der Schnittstellendefinition festgelegten Methoden- oder Eigenschaftsnamen.

Von der Klasse zur Schnittstelle

Wenn Sie bereits eine Klasse verwenden, welche die Eigenschaften und Methoden der geplanten Schnittstelle enthält, können Sie daraus ganz einfach die Schnittstelle erstellen: Werfen Sie einfach jede Codezeile mit Ausnahme von Option Explicit und gegebenenfalls Option Database sowie der eigentlichen Gerüste der öffentlichen Methoden und Eigenschaften raus. Wenn Sie die Klasse aus Listing 1 derart bearbeiten, bleibt der in Listing 3 dargestellte Rest. Diese Klasse speichern Sie unter dem Namen ISendMail. Das I steht für Interface, also Schnittstelle, und deutet darauf hin, dass diese Klasse nur die Schnittstellenbeschreibung und keinerlei Implementierung irgendwelcher Funktionen enthält.

Listing 3: Definition der Schnittstelle für die Klassen zum Versenden von E-Mails

Option Compare Database
Option Explicit
Public Property Let Sender(strSender As String)
End Property
Public Property Let Recipient(strRecipient As String)
End Property
Public Property Let Subject(strSubject As String)
End Property
Public Property Let Body(strBody As String)
End Property
Public Function SendMail() As Boolean
End Function

Der einzige Unterschied in der Deklaration der Prozedurköpfe ist der, dass wir die Function-Prozedur SendSMTPMail in SendMail umbenannt haben. Der Grund ist klar: Die Schnittstelle soll später stellvertretend für beide vorhandenen oder sogar für noch weitere Klassen verwendet werden und deshalb neutrale, von der verwendeten Methode unabhängige Bezeichnungen aufweisen.

Anpassen der Implementierungen

Damit wir die beiden Klassen clsSendOutlookMail und clsSendSMTPMail später als Implementierung nutzen können, müssen wir diesen mitteilen, dass sie die Schnittstelle ISendMail implementieren.

Wenn Sie dies mit einer vorhandenen Klasse durchführen, brauchen Sie dieser zunächst nur die folgende Zeile im Kopf des Moduls hinzuzufügen:

Implements ISendMail

Um zu prüfen, ob die Klasse alle in der Schnittstelle vorgegebenen Elemente implementiert, brauchen Sie nur das Projekt zu kompilieren (VBA-Editor, Menüeintrag Debuggen|Kompilieren von <Projektname>). Das Ergebnis des ersten Anlaufs sehen Sie in Bild 1. Die Implementierung der Property-Prozedur Sender ist nicht in Ordnung. Die Lösung ist einfach: Alle Elemente der Implementierungen einer Schnittstelle müssen mit dem Namen der Schnittstellenklasse und einem Unterstrich beginnen, also beispielsweise ISendMail_Sender statt einfach nur Sender.

pic001.png

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar