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.
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:
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/catalog/index.html
Refactoring sollte unter zwei Voraussetzungen erfolgen:
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.
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:
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
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:
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
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:
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo