Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 6/2004.

Unser Angebot für Sie!

Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'!

Diesen Beitrag twittern

Refactoring

André Minhorst, Duisburg

Refactoring ist eines der Schlagwörter des Extreme Programming und untrennbar mit dem Unit-Testing und der testgetriebenen Entwicklung verbunden. Durch Refactoring verbessern Sie die Struktur und Lesbarkeit des Codes einer Anwendung unter Beibehaltung der Funktionalität und erleichtern damit Änderungen und Erweiterungen der Funktion der Anwendung.

Grundlagen

Refactoring ist eine Tätigkeit, die Chefs und Managern vermutlich nicht gefallen wird, weil sie erstmal keinen Gewinn bringt und ein Projekt nicht sichtbar vorantreibt - immerhin bezeichnet man Refactoring ausdrücklich als Vorgehensweise, die sich nicht auf die Funktion einer Anwendung auswirkt. Genau genommen kann man es als Aufräumen bezeichnen - man sortiert hier ein wenig aus, stellt dort ein wenig um und wirft vielleicht sogar ein paar Dinge weg. Die Analogie zum wirklichen Leben hinkt nur insofern, als dass Sie nicht Platz für etwas Neues, sondern die Voraussetzungen für dessen einfache Integration schaffen.

Refactoring optimiert noch nicht einmal die Performance - im Gegenteil: Viele Refactoring-Maßnahmen führen zu einer Verlangsamung der betroffenen Teile um wenige Prozentpunkte.

Aber - und das ist das Wichtigste - Sie bereiten den Quellcode durch Refactoring auf die Anwendung von Performance-Optimierungen vor, weil Sie diesen wesentlich besser lesbar machen und damit das Erkennen und Beseitigen von Performance-Bremsen erleichtern.

Die wichtigsten Ziele des Refactoring sind folgende:

  • Der Code soll für Menschen besser lesbar gemacht werden.
  • Die Struktur (Architektur) des Codes soll verbessert werden.
  • Der Code soll keine redundanten Abschnitte enthalten.
  • Der Code soll von Fehlern befreit und das Auffinden von Fehlern erleichtert werden.
  • Die Anwendung soll robuster werden.
  • Refactoring besteht aus einem oder mehreren Refactoring-Maßnahmen - je nachdem, wie gute Vorarbeit man geleistet hat und wie viel Verbesserungspozential der Code noch bietet. Mit diesen Maßnahmen können Sie den Code Stück für Stück besser strukturieren und lesbarer machen.

    Refactoring ist in Zusammenhang mit objektorientierten Programmiersprachen entstanden. Da VBA nicht alle Aspekte der Objektorientiertheit beinhaltet, lassen sich längst nicht alle in Literatur und Internet beschriebenen Refactoring-Maßnahmen auf VBA-Code anwenden.

    In den nachfolgenden Kapiteln lernen Sie ausschließlich solche Refactorings kennen, die für mit VBA entwickelte Anwendungen geeignet sind. Zu jedem Refactoring finden Sie ein Beispiel und - soweit sinnvoll - eine Abbildung zur Veranschaulichung der Vorgehensweise.

    In manchen Fällen stellen wir auch Refactorings vor, die in der Literatur nicht beschrieben sind - beispielsweise die Umwandlung einer Funktion mit mehreren Rückgabeparametern in eine Klasse mit entsprechenden Eigenschaften.

    Unbewusst werden Sie vielleicht schon das eine oder andere Refactoring an Ihrem Code vorgenommen haben, weil es Ihnen einfach sinnvoll erschien. Ein gutes Beispiel dafür ist das Extrahieren einer Methode, bei dem man eine oder mehrere Zeilen Code, die an mehreren Stellen in einer oder mehreren Prozeduren vorkommen, in eine neue Prozedur ausgliedert.

    Hinweis

    Im Internet gibt es eine Menge Informationen zum Thema Refactoring, meist in Verbindung mit objektorientierten Sprachen wie Java oder VB.NET. Wenn Sie im Internet nach zusätzlichem Informationsmaterial suchen, sollten Sie als Suchbegriff zusätzlich noch den Namen Martin Fowler eingeben. Die in diesem Beitrag und den geplanten Fortsetzungen vorgestellten Refactoring-Maßnahmen lehnen sich an die auf der folgenden Internetseite aufgeführten Informationen an und sind den speziellen Gegebenheiten unter Microsoft Access beziehungsweise VBA angepasst: http://www.refactoring.com/cata
    log/index.html (

    Voraussetzungen

    Refactoring sollte unter zwei Voraussetzungen erfolgen:

  • Der zu refaktorierende Code ist - soweit wie unter VBA möglich - objektorientiert.
  • Es gibt automatisierte Tests für den zu ändernden Code, mit dem man den Code vor und nach einer Refactoring-Maßnahme testen kann.
  • Objektorientierung

    Die erste Voraussetzung ist nirgends festgelegt, weil Refactoring ohnehin aus der objektorientierten Programmierung stammt und dort zusammenhängende Methoden und Eigenschaften in Klassenmodulen untergebracht sind. Standardmodule wie in VBA werden dort nicht verwendet. Viele Refactoring-Maßnahmen beziehen sich ausdrücklich auf Klassen. Es gibt zwar einige, die auf Eigenschafts- oder Methodenebene anwendbar und damit auch für VBA-Prozeduren in Standardmodulen geeignet sind, aber die Kapselung von Funktionalität in Klassen kommt dem Refactoring-Prozess in den meisten Fällen entgegen.

    Tests

    Die zweite Voraussetzung ist die Absicherung der vom Refactoring betroffenen Funktionalität. Und hier kommen die weiter oben erwähnten Unit-Tests und die testgetriebene Entwicklung ins Spiel: Wenn Sie die Anforderungen an die Eigenschaften und Methoden durch das vorherige Schreiben von Tests festlegen und auch überprüfen, können Sie auch ohne Sorge Änderungen in Form von Refactoring-Maßnahmen durchführen. Dazu stellen Sie einfach sicher, dass die Anwendung vor dem Refactoring funktioniert, führen die Änderungen durch und testen anschließend, ob weiterhin alles reibungslos läuft. Dazu können Sie die Tests verwenden, mit denen Sie auch die entsprechenden Methoden entwickelt haben - der einzige Unterschied ist, dass die Tests in diesem Zusammenhang "Regressionstests" genannt werden.

    Testen Sie so oft wie möglich - je kleiner die Änderungen zwischen zwei Tests sind, umso schneller finden Sie eventuell auftretende Fehler.

    Hinweis

    Weitere Informationen über die testgetriebene Entwicklung finden Sie im Beitrag Testgetriebene Entwicklung mit Access in Ausgabe 4/2004 von Access im Unternehmen. (

    In den folgenden Abschnitten geht es los: Sie lernen die ersten Refactoring-Maßnahmen kennen. In diesem Beitrag stellen wir Ihnen gängige Maßnahmen für das Refactoring von Variablen und Methoden vor.

    Refactoring mit Variablen

    Variablen bieten gute Ansatzpunkte für Refactoring. Nachfolgend finden Sie einige Refactoring-Maßnahmen, die je nach Situation Variablen hinzufügen, entfernen oder einfach ändern.

    Public Sub EinsBisZehn()

        Dim i As Integer

        For i = 1 To 10

            Debug.Print i

        Next i

    End Sub

    Quellcode 1

    Private Sub KombinationsfeldFuellen(intErstesJahr As Integer, intLetztesJahr As Integer)

        Dim i As Integer

        Dim strJahre As String

        For i = intErstesJahr To intLetztesJahr

            strJahre = strJahre & i & ";"

        Next i

        strJahre = Mid(strJahre, 1, Len(strJahre) - 1)

        Me.cboJahreszahlen.RowSource = strJahre

    End Sub

    Quellcode 2

    Public Sub Rechteck(a As Integer, b As Integer)

        Dim temp As Integer

        temp = a * b

        Debug.Print "Fläche: " & temp

        temp = 2 * (a + b)

        Debug.Print "Umfang: " & temp

    End Sub

    Quellcode 3

    Hinweis

    In den Schritt-für-Schritt-Anweisungen der einzelnen Refactoring-Maßnahmen finden Sie oft den Hinweis, die Anwendung zu kompilieren. Damit sollen meist bei der Änderung von Variablen vergessene Vorkommen der jeweiligen Variablen gefunden werden. Damit der Debugger sich meldet, wenn eine nicht deklarierte Variable im Quellcode enthalten ist, müssen Sie zu Beginn des Moduls die Zeile Option Explicit einfügen. Es dürfen dann keine Variablen mehr ohne Deklaration verwendet werden. (

    Temporäre Variable zerlegen

    Es gibt mehrere Möglichkeiten, temporäre Variablen mehrfach zu verwenden. Die erste Möglichkeit ist die Verwendung in einer Schleife, was vollkommen legitim und außerdem unausweichlich ist, wie das Beispiel aus Quellcode 1 zeigt.

    Die zweite Möglichkeit ist gegeben, wenn der Inhalt der Variablen sukzessiv erweitert wird, etwa um den Inhalt eines Kombinationsfeldes zu erzeugen (im Fall des Beispiels aus Quellcode 2 handelt es sich um Jahreszahlen).

    Die dritte Möglichkeit besteht in der Verwendung einer Variablen für verschiedene Zwecke, wie in dem Beispiel aus Quellcode 3.

    Die Methode speichert nacheinander zwei völlig unterschiedliche Informationen in einer Variablen.

    Im vorliegenden Fall ist das noch überschaubar, aber wenn Sie sich noch 30 Zeilen Code zwischen den beiden Verwendungen der Variablen temp vorstellen, nimmt die Übersichtlichkeit rasch ab.

    Beschreibung der
    Vorgehensweise

    Verwenden Sie jede Variable nur einmal und vergeben Sie außerdem sprechende Namen für jede Variable. Damit erhöhen Sie die Übersichtlichkeit und können außerdem später auf alle temporären Variablen zugreifen, ohne deren Werte erneut ermitteln zu müssen. Gehen Sie folgendermaßen vor:

  • Benennen Sie die temporäre Variable in der Deklaration nach dem Zweck des ersten Vorkommens.
  • Ändern Sie den Variablennamen an allen Stellen im Code, die sich auf die erste temporäre Variable beziehen.
  • Public Function AnzahlPersonen()

        AnzahlPersonen = DCount("[Kunden-Code]", "Kunden") _
            + DCount("[Personal-Nr]", "Personal")

    End Function

    Quellcode 4

    Public Function AnzahlPersonen()

        Dim intAnzahlKunden As Integer

        Dim intAnzahlMitarbeiter As Integer

        intAnzahlKunden = DCount("[Kunden-Code]", "Kunden")

        intAnzahlMitarbeiter = DCount("[Personal-Nr]", "Personal")

        AnzahlPersonen = intAnzahlKunden + intAnzahlMitarbeiter

    End Function

    Quellcode 5

  • Deklarieren Sie eine neue Variable mit einem Namen, der sich nach dem Zweck des zweiten Auftretens dieser Variablen richtet.
  • Passen Sie den Namen der Variablen im Code an.
  • Verfahren Sie genauso für alle weiteren Vorkommen.
  • Kompilieren Sie um sicherzugehen, dass Sie die ursprüngliche Variable an allen Stellen im Code ersetzt haben. (
  • Die Beispielprozedur aus Quellcode 3 sieht nach dieser Behandlung wie folgt aus (Änderungen fett gedruckt):

    Public Sub Rechteck(a As Integer, b As Integer)

        Dim intFlaeche As Integer

        Dim intUmfang As Integer

        intFlaeche = a * b

        Debug.Print "Fläche: " & intFlaeche

        intUmfang = 2 * (a + b)

        Debug.Print "Umfang: " & intUmfang

    End Sub 

    Erklärende Variable einführen

    Man verwendet oft komplizierte und aus mehreren Teilen bestehende Ausdrücke in Methoden, deren Ergebnis nicht unbedingt offensichtlich ist.

    Auf eine Domänenfunktion müssen Sie schon einen genaueren Blick werfen, um genau zu erkennen, welche Information sie ermittelt, um welche Domänenfunktion es sich handelt (DLookup, DMax, DCount, ...), auf welche Datenherkunft sie sich bezieht, welches Feld die Funktion betrifft und wie ein eventuell vorhandenes Kriterium die Datenherkunft einschränkt. Folgende Funktion soll beispielsweise die Gesamtanzahl der Mitarbeiter und Kunden einer Firma ermitteln - zum Beispiel, um die richtige Menge Weihnachtskarten zu beschaffen (s. Quellcode 4).

    Zugegeben: Die Funktion lässt sich noch relativ schnell erfassen. Folgende Variante ist aber wesentlich freundlicher. Sie sehen, dass dort mit irgendeiner Funktion die Anzahl der Kunden und mit einer weiteren Funktion die Anzahl der Mitarbeiter ermittelt wird und dass diese beiden Werte addiert werden (s. Quellcode 5).

    Beschreibung der Vorgehensweise

    Diese Refactoring-Maßnahme enthält folgende Schritte:

  • Deklarieren Sie eine temporäre Variable mit einem aussagekräftigen Namen für jede Teilfunktion, für die es lohnenswert erscheint.
  • Weisen Sie den temporären Variablen den entsprechenden Teilausdruck zu.
  • Public Function AnzahlPersonen()

        Dim intAnzahlKunden As Integer

        Dim intAnzahlMitarbeiter As Integer

        intAnzahlKunden = AnzahlKunden

        intAnzahlMitarbeiter = DCount("[Personal-Nr]", "Personal")

        AnzahlPersonen = intAnzahlKunden + intAnzahlMitarbeiter

    End Function

    Private Function AnzahlKunden() As Integer

        AnzahlKunden = DCount("[Kunden-Code]", "Kunden")

    End Function

    Quellcode 6

    Public Function AnzahlPersonen()

        Dim intAnzahlMitarbeiter As Integer

        intAnzahlMitarbeiter = DCount("[Personal-Nr]", "Personal")

        AnzahlPersonen = AnzahlKunden + intAnzahlMitarbeiter

    End Function

    Quellcode 7

    Private Function AnzahlMitarbeiter() As Integer

        AnzahlMitarbeiter = DCount("[Personal-Nr]", "Personal")

    End Function

    Quellcode 8

    Public Function AnzahlPersonen()

        AnzahlPersonen = AnzahlKunden + AnzahlMitarbeiter

    End Function

    Quellcode 9

  • Setzen Sie den ursprünglichen Ausdruck aus den Teilausdrücken zusammen. (
  • Temporäre Variable durch Funktion ersetzen

    Diese Refactoring-Maßnahme heißt eigentlich Temporäre Variable durch Abfrage ersetzen. Mit Abfrage ist hier allerdings keine SQL-Abfrage, sondern das Pendant anderer Programmiersprachen zu einer VBA-Funktion gemeint.

    Als Beispiel greifen wir die Funktion auf, die durch die Refactoring-Maßnahme Erklärende Variable einführen entstanden ist (s. Quellcode 5). Voraussetzung ist weiterhin, dass die Methode der temporären Variablen nur einmal einen Wert zuweist. Ist das gegeben, gehen Sie beispielsweise folgendermaßen vor:

    Erstellen Sie eine private Funktion für die Ermittlung der Anzahl der Kunden und ersetzen Sie die DCount-Anweisung in der ursprünglichen Funktion durch die neue Funktion (s. Quellcode 6).

    Setzen Sie die Funktion AnzahlKunden überall ein, wo dieser Wert benötigt wird - außer bei der temporären Variablen intAnzahlKunden. Diese entfernen Sie ebenso wie die entsprechende Zuweisung (s. Quellcode 7).

    Erstellen Sie eine entsprechende private Funktion für die Variable intAnzahlMitarbeiter wie in Quellcode 8 und verwenden Sie den Aufruf dieser Funktion statt der Variablen wie in Quellcode 9.

    Damit hat die ursprüngliche Funktion deutlich an Gewicht verloren und ist wesentlich überschaubarer geworden. Zusätzlich haben Sie zwei Funktionen zur Ermitlung der Anzahl der Mitarbeiter und Kunden gewonnen, die Sie auch an anderer Stelle in dieser Klasse einsetzen können.

    Public Sub SpielereiMitNamen(strName As String)

        Dim strVorname As String

        Dim strNachname As String

        Call NameAufteilen(strName, strVorname, strNachname)

        Debug.Print "Vorname: " & strVorname

        Debug.Print "Nachname: " & strNachname

    End Sub

    Public Sub NameAufteilen(strName As String, _
        strVorname As String, strNachname As String)

        Dim intPos As Integer

        intPos = InStr(1, strName, " ")

        strVorname = Mid(strName, 1, intPos)

        strNachname = Mid(strName, intPos + 1)

    End Sub

    Quellcode 10

    Public Sub SpielereiMitNamen_OO(strName As String)

        Dim objPerson As clsPerson

        Set objPerson = New clsPerson

        objPerson.NameAufteilen strName

        Debug.Print "Vorname: " & objPerson.Vorname

        Debug.Print "Nachname: " & objPerson.Nachname

        Set objPerson = Nothing

    End Sub

    Quellcode 11

    Beschreibung der Vorgehensweise

    Mit den nachfolgenden Schritten ersetzen Sie eine Variable durch eine Funktion:

  • Erzeugen Sie eine private Funktion, die den der Variablen zugewiesenen Inhalt ermittelt.
  • Ersetzen Sie die Variable überall dort, wo ihr Wert verwendet wird, durch den Funktionsaufruf.
  • Entfernen Sie die Deklaration der Variablen und die ursprüngliche Zuweisung. (
  • Zuweisung an Parameter entfernen

    Parameter von Methoden dienen entweder dazu, einen Wert von der aufrufenden an die aufgerufene Methode zu übergeben oder umgekehrt oder beides. Genau von dem jeweiligen Fall sollte man auch abhängig machen, wie man einen solchen Parameter behandelt.

    Den letzteren Fall können Sie, wenn Sie objektorientiert und dementsprechend mit Klassen arbeiten möchten, ausschließen. Damit hätten Sie hier ein Refactoring, das in den üblichen Katalogen nicht beschrieben sein dürfte.

    Die Vorgehensweise, Ergebnisse einer Methode über die Parameter an die aufrufende Methode zu übergeben, ist immer dann angezeigt, wenn mehr als ein Parameter zurückgegeben werden und kein Array oder ein Type dafür verwendet werden soll. Die folgende Prozedur SpielereiMitNamen (s. Quellcode 10) ruft die Prozedur NameAufteilen auf, die den übergebenen Namen aufteilt und die resultierenden Teile in die Parameter strVorname und strNachname schreibt. Die aufrufende Prozedur kann diese anschließend auswerten.

    Mit einer Klasse sind keine Parameter mehr erforderlich, wenn eine Methode mehrere Rückgabewerte enthält. Diese Aufgabe übernehmen dann öffentliche Eigenschaften mit Lesezugriff. Das folgende Beispiel zeigt, wie es funktioniert. Die aufrufende Routine sieht so wie in Quellcode 11 aus.

    Die Routine deklariert und instanziiert ein Objekt, das auf der Klasse clsPerson basiert. Sie ruft deren Methode NameAufteilen mit dem kompletten Namen als Parameter auf und ermittelt aus den entsprechenden öffentlichen Eigenschaften die Werte für den Vor- und den Nachnamen.

    Dim mVorname As String

    Dim mNachname As String

    Public Property Get Vorname() As String

        Vorname = mVorname

    End Property

    Public Property Get Nachname() As String

        Nachname = mNachname

    End Property

    Public Sub NameAufteilen(strName As String)

        Dim intPos As Integer

        intPos = InStr(1, strName, " ")

        mVorname = Mid(strName, 1, intPos)

        mNachname = Mid(strName, intPos + 1)

    End Sub

    Quellcode 12

    Public Sub NameGrossSchreiben(strName As String)

        Dim objPerson As clsPerson

        Set objPerson = New clsPerson

        Debug.Print objPerson.NameGrossSchreiben(strName)

        Debug.Print strName

        Set objPerson = Nothing

    End Sub

    Quellcode 13

    Public Function NameGrossSchreiben(strName As String)

        strName = StrConv(strName, vbProperCase)

        NameGrossSchreiben = strName

    End Function

    Quellcode 14

    Das instanziierte Objekt basiert auf einer Klasse, die ein Klassenmodul namens clsPerson mit folgendem Inhalt besitzt (s. Quellcode 12).

    Die beiden vorherigen Parameter zur Übergabe von Vor- und Nachname sind nun als öffentliche Eigenschaften der Klasse ausgeführt. Sie sollen nur gelesen werden, daher gibt es je eine Property Get-Prozedur für jede Eigenschaft. Der aufzuteilende Name wird der Methode NameAufteilen per Parameter mitgeteilt.

    Hinweis

    Die in diesem Beispiel beschriebene Umwandlung einer Prozedur mit Rückgabeparametern in eine Klasse mit speziell für die Rückgabe von Ergebnissen angelegten Eigenschaften kann aus Platzgründen hier leider nicht im Detail beschrieben werden. Weitere Informationen zur Verwendung von Klassen in Access finden Sie im Beitrag Objektorientiert programmieren mit Klassen in der Ausgabe 4/2004 von Access im Unternehmen. (

    Das eigentliche Motiv der Refactoring-Maßnahme Zuweisung an Parameter entfernen ist aber noch anders gelagert:

    Ausgangspunkt ist eine Methode, die den Wert eines Eingangsparameters verändert.

    Das kann gerade unter VBA gefährlich werden, da solche Werte im Normalfall als Referenz (ByRef) und nicht als Wert (ByVal) übergeben werden. Somit wirken sich - wie in obigem und auch in folgendem Beispiel - Änderungen an den Parametern bis auf die aufrufende Routine aus.

    Den Aufruf der Methode können Sie Quellcode 13 entnehmen.

    Und nachfolgend finden Sie die entsprechende Methode in der bereits oben vorgestellten Klasse clsPerson (s. Quellcode 14).

    In dieser Konstellation wird der Parameter strName verändert, was sich auch auf die entsprechende Variable der aufrufenden Routine auswirkt. Um das zu verhindern, verwenden Sie einfach eine Hilfsvariable wie im Beispiel aus Quellcode 15. In dieser speichern Sie zunächst den Eingangswert zwischen, und beziehen sich im Anschluss auf die Variable, die den Wert nun enthält.

    Public Function NameGrossSchreiben(strName As String)

        Dim strTemp As String

        strTemp = StrConv(strName, vbProperCase)

        NameGrossSchreiben = strTemp

    End Function

    Quellcode 15

    Beschreibung der Vorgehensweise

    Die nachfolgende Beschreibung bezieht sich auf den im letzten Beispiel dargestellten Fall.

  • Deklarieren Sie eine temporäre Variable in der Methode.
  • Ersetzen Sie den Parameter jedes Mal, wenn ihm ein Wert zugewiesen wird, durch die temporäre Variable. (
  • Temporäre Variable entfernen

    Temporäre Variablen sind manchmal nicht wirklich notwendig. Das ist meist der Fall, wenn der Inhalt des zugewiesenen Ausdrucks durch den Ausdruck ausreichend beschrieben wird - also beispielsweise, wenn Sie einer Variablen den Inhalt eines Feldes einer Datensatzgruppe zuweisen (s. Quellcode 16).

    Manchmal wird eine temporäre Variable auch erst mit der Zeit überflüssig - etwa wenn Sie den in der Variablen gespeicherten Wert zu Beginn oft benötigt haben, dessen Einsätze aber nach und nach bis auf einen zusammengestrichen haben.

    Eine Variable wie strFirma etwa macht nur dann Sinn, wenn der Inhalt nach der Zuweisung mehr als einmal verwendet wird. Ansonsten sollten Sie diese - ausgehend von Quellcode 16 - streichen und die Zuweisung direkt vornehmen wie in Quellcode 17.

    Beschreibung der
    Vorgehensweise

    Wenn eine Variable entfernt werden soll, gehen Sie folgendermaßen vor:

  • Ersetzen Sie die Variable an allen Stellen des Auftretens durch den dieser Variablen zugewiesenen Ausdruck.
  • Kommentieren Sie die Deklaration der Variablen und die Zuweisung des Ausdrucks an die Variable aus.
  • Kompilieren Sie, um sicherzustellen, dass Sie kein Auftreten der Variablen übersehen haben. (
  • '...

    Dim strFirma As String

    '...

    Do While Not rst.EOF

        strFirma = rst!Firma

        Debug.Print strFirma

        rst.MoveNext

    Loop 

    '...

    Quellcode 16

    '...

    Do While Not rst.EOF

        Debug.Print rst!Firma

        rst.MoveNext

    Loop 

    '...

    Quellcode 17

    Refactoring mit Methoden

    Nachdem Sie sich mit verschiedenen Refactoring-Maßnahmen in Zusammenhang mit Variablen befasst haben, folgen nun größere Bestandteile von Methoden: nämlich solche, die sich komplett aus- oder auch eingliedern lassen. Natürlich spielen Variablen auch hier wieder eine Rolle, sie können das Ausgliedern von Code erheblich erschweren.

    Public Sub MethodeExtrahieren_I()

        If SysCmd(acSysCmdGetObjectState, acForm, "frmBeispiel") Then

            '...

        End If

        '...

        If SysCmd(acSysCmdGetObjectState, acForm, "frmBeispiel") Then

            '...

        End If

    End Sub

    Quellcode 18

    Extrahieren von Methoden

    Der Name dieser Refactoring-Maßnahme ist möglicherweise etwas irreführend, da diese eigentlich das Erstellen neuer Methoden aus einer oder mehreren Anweisungen einer bestehenden Methode beinhaltet.

    Für diese Vorgehensweise kann es mehrere Gründe geben:

  • Eine Anweisung oder mehrere zusammenhängende Anweisungen wiederholen sich.
  • Eine Methode ist zu lang und enthält Anweisungen, die man gruppieren und in einer eigenen Methode unterbringen kann.
  • Wiederholung von Anweisungen oder Gruppen von Anweisungen

    Erstere Variante ist sicher jedem schon mal untergekommen, im einfachsten Fall in Form eines Einzeilers.

    Folgendes Beispiel überprüft beispielsweise zweimal, ob ein bestimmtes Formular geöffnet ist, und verwendet dazu eine eingebaute Funktion mit wenig aussagekräftigem Namen, deren Zweck sich erst aus dem Studium der Parameter ergibt (s. Quellcode 18).

  • Hier gibt es mindestens drei Argumente für ein Extrahieren dieser Anweisung in eine eigene Methode:
  • Die Funktion hat einen aussagekräftigeren Namen verdient.
  • Die Funktion tritt mehrmals auf, der Pflegeaufwand könnte reduziert werden.
  • Die Funktion könnte man noch erweitern, um etwa die aktuelle Ansicht des Formulars zu ermitteln - das erfordert wesentlich weniger Aufwand, wenn es nur an einer Stelle erfolgen muss.
  • Der exakt gleiche Aufruf der SysCmd-Funktion in diesem Beispiel könnte auf den ersten Blick auch dazu verleiten, eine temporäre Variable für das Ergebnis zu verwenden und die Funktion nur einmal aufzurufen - das wäre aber sehr gefährlich, da sich der Zustand des Formulars durchaus ändern könnte, während die Routine abläuft.

    Beschreibung der Vorgehensweise

    Um die betreffenden Anweisungen in eine neue Methode zu extrahieren, gehen Sie folgendermaßen vor:

  • Erstellen Sie eine neue Methode mit dem gewünschten Namen. Verwenden Sie dabei eine Function- oder eine Sub-Prozedur - je nachdem, ob die Anweisung(en) einen Rückgabewert liefern.
  • Kontrollieren Sie, ob temporäre Variablen der alten Methode sowohl von den verbleibenden als auch von den zu extrahierenden Anweisungen verwendet werden. Fügen Sie diese der Methode als Parameter hinzu.
  • Kontrollieren Sie, ob temporäre Variablen der alten Methode nur in den zu extrahierenden Anweisungen verwendet werden, und extrahieren Sie diese gegebenenfalls mit.
  • Public Function IstfrmBeispielGeoeffnet()

        IstfrmBeispielGeoeffnet = SysCmd(acSysCmdGetObjectState, acForm, "frmBeispiel")

    End Function

    Quellcode 19

    Public Function IstFormularGeoeffnet(strFormular As String)

        IstFormularGeoeffnet = SysCmd(acSysCmdGetObjectState, acForm, strFormular)

    End Function

    Quellcode 20

  • Fügen Sie die zu extrahierenden Anweisungen in die neue Methode ein.
  • Löschen Sie die Anweisungen in der alten Methode.
  • Fügen Sie den Aufruf der neuen Methode an Stelle der extrahierten Anweisungen ein. Vergessen Sie nicht, die Parameter anzugeben. (
  • Für unser Beispiel sieht das Ergebnis folgendermaßen aus - zuerst die alte Methode:

    Public Sub MethodeExtrahieren_II()

        If IstfrmBeispielGeoeffnet Then

            '...

        End If

        '...

        If IstfrmBeispielGeoeffnet Then

            '...

        End If

    End Sub

    Die neue Methode sieht nun wie in Quellcode 19 aus. Für diesen Zweck wäre diese Konfiguration durchaus ausreichend, aber es macht natürlich keinen Sinn, eine Methode zu verwenden, die den Zustand genau eines festgelegten Formulars ermittelt.

    Damit die Funktion auch mit anderen Formularen arbeitet, ersetzen Sie den Namen des zu untersuchenden Formulars durch einen Parameter (s. Quellcode 20).

    Der Aufruf in der alten Methode sieht nun so aus:

    If IstFormularGeoeffnet("frmBeispiel") _
        Then

        '...

    End If

    Lange und unübersichtliche Methoden aufteilen

    Das Extrahieren von Methoden dient nicht zwangsläufig nur zum "Ausklammern" von Codeteilen. Es kann auch oft hilfreich sein, wenn eine Methode zu lang ist, um schnell verstanden zu werden. Ob und wann das so ist, können Sie schnell herausfinden, indem Sie prüfen, ob der enthaltene Code dem Namen der Methode entspricht oder ob dort vielleicht viel mehr passiert, als der Name der Prozedur erahnen lässt. Wenn das der Fall ist, sollten Sie alle entsprechenden Teile des Codes in Methoden mit aussagekräftigen Namen extrahieren.

    Integrieren von Methoden

    Der Inhalt mancher Methode ist so einfach und selbsterklärend, dass eigentlich keine Methode mit einem eigenen Namen dafür nötig ist.

    In manchen Fällen ist es daher sinnvoll, eine Methode aufzulösen und die darin enthaltenen Anweisungen in die aufrufende Methode zu integrieren.

    Ob man eine Methode in eine andere integriert, hängt auch davon ab, an wie vielen Stellen auf diese verwiesen wird. Passiert das oft, verlieren Sie durch die Integration die Möglichkeit, Änderungen komfortabel an einer Stelle durchzuführen.

    Public Function BeispielMethodeIntegrieren()

        Dim intSumme As Integer

        Dim intA As Integer

        Dim intB As Integer

        intA = 10

        intB = 5

        '...

        intSumme = Summe(intA, intB)

        Debug.Print intSumme

    End Function

    Private Function Summe(intA As Integer, intB As Integer)

        Summe = intA + intB

    End Function

    Quellcode 21

    Public Function BeispielMethodeIntegrieren()

        Dim intSumme As Integer

        Dim intA As Integer

        Dim intB As Integer

        intA = 10

        intB = 5

        '...

        intSumme = intA + intB

        Debug.Print intSumme

    End Function

    Quellcode 22

    Ist die Funktion der Methode hingegen so speziell, dass sie nur in einer Methode und dort auch nur wenige Male aufgerufen wird, kann man sie dort guten Gewissens integrieren. Das Beispiel aus Quellcode 21 enthält den Aufruf einer mehr als trivialen Funktion, die dringend in die aufrufende Methode integriert werden sollte. Hier ersetzen Sie einfach den rechten Teil des Aufrufs durch den rechten Teil der einzigen Anweisung der Funktion (s. Quellcode 22). Die Funktion Summe können Sie anschließend entfernen.

    Beschreibung der Vorgehensweise

    Die Anwendung dieser Refactoring-Maßnahme umfasst folgende Schritte:

  • Ersetzen Sie alle Aufrufe durch den Inhalt der zu integrierenden Methode.
  • Kommentieren Sie die zu integrierende Methode aus.
  • Kompilieren Sie, um zu prüfen, ob alle Aufrufe der Methode durch deren Inhalt ersetzt wurden.
  • Entfernen Sie die auskommentierte Methode entgültig. (
  • Achten Sie darauf, dass korrespondierende Parameter im Aufruf und in der ersten Zeile der Methode theoretisch unterschiedliche Namen haben können.

    Zusammenfassung und Ausblick

    Im diesem Beitrag haben wir Ihnen einige Maßnahmen zur Verbesserung des Quellcodes vorgestellt, von denen Sie die eine oder andere - bewusst oder unbewusst - schon verwendet haben. Wenn Sie die Refactoring-Maßnahmen konsequent anwenden, erhalten Sie gut lesbaren Code, den auch andere Entwickler leicht warten und weiterentwickeln können.

    Hin und wieder sollten Sie die vorgenommen Änderungen hinsichtlich eventueller Performance-Einbußen kontrollieren. In der Regel liegen diese aber in vernachlässigbaren Bereichen und können in Kauf genommen werden.

    In den folgenden Ausgaben von Access im Unternehmen stellen wir Ihnen in loser Folge weitere Refactoring-Maßnahmen vor.

    Kompletten Artikel lesen?

    Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

    E-Mail:

    © 2003-2015 André Minhorst Alle Rechte vorbehalten.