Refactoring: Code optimieren - Teil 1

15.03.2006
Wer ist noch nicht in einen Programmierrausch gefallen und hat Zeile um Zeile und Prozedur um Prozedur eingehackt? Wenn man einmal so richtig in Fahrt ist, kann mitunter die Qualität des Codes leiden: Heraus kommt dann Spaghetti- Code, den selbst der Verfasser schon wenige Tage später kaum mehr zu entwirren weiß. Um dies zu verhindern, sollten Sie rechtzeitig Pausen einlegen und den Code reviewen. Wir zeigen Ihnen, welche Möglichkeiten sich hier anbieten.

Manch eine Prozedur erreicht schnell eine Länge von mehreren hundert Zeilen – ohne Leerzeilen und Kommentare. Hier den Überblick zu behalten ist schon schwer, aber nach einiger Zeit Änderungen vorzunehmen oder Funktionalität hinzuzufügen ist eine Strafe. Strafe dafür, dass man den Code nicht anständig strukturiert, um ihn jederzeit ohne Einarbeitungszeit überarbeiten zu können.

In diesem Beitrag erwartet Sie eine Erläuterung von Möglichkeiten, wie man seinen Code optimieren kann, ohne seine von außen sichtbare Funktion zu ändern. Die jeweiligen Möglichkeiten sind immer mit einem repräsentativen Beispiel verknüpft, es gibt jedoch für alle dieser Aktionen mehr als nur den beispielhaft angeführten Anlass.

Refactoring

Ein oft verwendeter Begriff für das Optimieren von Code ist „Refactoring“, auch „Bereinigen“ trifft gewissermaßen den Punkt. Diese Begriffe umschreiben eine Tätigkeit, bei der das Design des Codes geändert wird, um diesen bezüglich folgender Aspekte zu optimieren:

Der wichtigste Punkt ist aber, dass die Funktionalität des Codes bei dieser Tätigkeit nicht verändert wird. Auch ist es nicht das Ziel, etwa die Performance zu steigern – auch wenn solche oder ähnliche Nebeneffekte durchaus auftreten können. Natürlich kann die Optimierung des Codes auch das Gegenteil bewirken. In jedem Fall aber erleichtert ein gut strukturierter und wartbarer Code weitere Optimierungen wie etwa der Performance.

Der passende Zeitpunkt zum Optimieren von Code

Eine der Kernaussagen des obigen Abschnitts ist, dass die Optimierung von Code keine Änderung der Funktionalität bewirkt. Das bedeutet, dass der Code vor der Optimierung in der gewünschten Weise funktioniert und dies nach der Optimierung auch noch tut, ohne dass sich die Anforderungen geändert haben. Das Anpassen der Anwendung an neue Anforderungen hat nichts mit dem Optimieren von Code zu tun – das ist nicht nur eine andere Liga, sondern eine ganz andere Sportart.

Um das Gelingen der Codeoptimierung sicherzustellen, müssen Sie die Funktionalität vor der und nach der Optimierung prüfen. Dafür sind in der Regel Unit-Tests verantwortlich, die zum automatischen Testen von Code verwendet werden. Diese Vorgehensweise ist jedoch eher in den objektorientierten Programmiersprachen angesiedelt und unter VB/VBA relativ wenig verbreitet. Außerdem würde das Thema den Rahmen dieses Beitrags sprengen. Gegebenenfalls erfahren Sie in einer der folgenden Ausgaben mehr darüber. Nach Codeoptimierungen sollten Sie in jedem Fall die Funktionen der Anwendungen erneut prüfen.

Am Anfang war …

Die vorangegangenen Zeilen mögen den Eindruck entstehen lassen, dass Code nach dem Schreiben erst einmal völlig unstrukturiert dasteht. Die nachfolgend genannten Vorschläge zur Optimierung von Code dürfen Sie natürlich gerne bereits bei der Erstellung des Codes einfließen lassen – wenn Sie dies nicht bereits intuitiv oder auf andere Weise motiviert tun. Das gilt vor allem für die folgenden Optimierungen.

Keine Namen ohne Regel

Wann immer Sie etwas in Ihrem Code bezeichnen – sei es eine Routine, eine Variable oder eine Konstante – sorgen Sie dafür, dass die Benennung nach von Ihnen festgelegten Regeln erfolgt. Das gilt nicht nur für den Code, sondern auch für die Objekte der Datenbank wie Tabellen, Abfragen, Formulare, Berichte und Klassen. Optimalerweise basiert der Regelsatz auf einem gängigen Standard, der im Falle von Access und VBA wohl die Ungarische Notation von Gregory Reddick sein dürfte. Die meisten Access-Entwickler haben die Ungarische Notation wohl bereits verinnerlicht (wenn auch mit der einen oder anderen Variation); falls dies bei Ihnen nicht der Fall ist, konsultieren Sie einfach einmal die folgende Internetadresse: http://www.xoc.net/standards/rvbanc.asp. Den Inhalt dieser Konvention soll der vorliegende Beitrag nicht wiederholen, aber die Motive für den Einsatz der Konvention werden kurz dargestellt.

Die Konvention schlägt vor, wie man Objekte unter Access/VBA benennen soll. Das bezieht sich vorrangig auf die Verwendung von Präfixen und Suffixen, aber auch auf die Zusammenstellung der eigentlichen Bezeichnung. Wenn Sie bei der Benennung einheitlich vorgehen und die Bezeichnung die Funktion beziehungsweise den Inhalt des betroffenen Elements treffend beschreibt, haben Sie zumindest den Vorteil, dass Sie vor der Verwendung einer bereits deklarierten Variablen nicht erst den Code nach dem richtigen Namen durchsuchen müssen. Der lässt sich dann nämlich aus der Funktion beziehungsweise dem Inhalt des jeweiligen Elements ableiten. Und wenn Sie dann noch einen allgemeinen Standard berücksichtigen, können Sie auch andere Entwickler ohne schlechtes Gewissen auf Ihren Code loslassen.

Das Auge programmiert mit

Wenn man bei Code von Struktur spricht, betrifft das nicht nur den Inhalt, sondern auch das Aussehen des Codes. Wenn Sie einen Entwickler ärgern wollen, der mit Ihrem Code arbeiten soll, lassen Sie einfach sämtliche Leerzeilen weg und verzichten entweder ganz auf Einrückungen oder setzen sie völlig willkürlich. Wenn Sie dann auch noch mehrere Anweisungen durch Doppelpunkte getrennt in eine einzige Zeile schreiben, dürfte Ihnen das vermutlich gelungen sein.

Im Ernst: Die Lesbarkeit von Code lässt sich mit wenigen Leerzeilen und Einrückungen drastisch verbessern – umso mehr, wenn Sie für die Einrückungen ein verbreitetes Maß als Tabulator-Schrittweite verwenden. Einstellen lassen sich solche Details übrigens im Optionen-Dialog des VBA-Editors, den Sie über den Menübefehl Extras/Optionen anzeigen (Bild 1).

Bild 1: Einstellungen des VBA-Editors.

In diesem Dialog befinden sich noch weitere für die Codequalität interessante Optionen: So sollten Sie beispielsweise die Option Variablendeklaration erforderlich aktivieren, damit jedes neue Modul automatisch mit der Zeile Option Explicit versehen wird. Auf diese Weise kommen Sie nicht in die Verlegenheit, mit einer Variablen zu arbeiten, die Sie zuvor nicht deklariert haben.
Auch die Option Automatisch Einzug vergrößern ist durchaus sinnvoll. Sie bewirkt, dass die Einfügemarke nach einem Zeilenumbruch von einer per Tab-Taste eingerückten Zeile automatisch wie die vorherige Zeile eingerückt wird. Die gängigen Regeln für das Einrücken von Zeilen sind schnell aufgezählt:

Leerzeilen können ab einer gewissen Anzahl Zeilen ebenfalls wichtig für die Lesbarkeit des Codes sein. Es hat allerdings keinen Sinn, zwischen jede Codezeile eine Leerzeile einzufügen. Leerzeilen sollten vielmehr zwischen den einzelnen Bereichen einer Routine eingefügt werden, zum Beispiel zwischen den Deklarationsteil und dem Rest der Routine.

Fortsetzung folgt

Wenn der VBA-Editor bei der Anzeige Ihrer Module den horizontalen Scrollbalken einblendet, sollten Sie sich um die überlangen Zeilen kümmern. Es reicht, wenn man im Code vertikal scrollen muss (an dieser Stelle ein Dankeschön an den Erfinder des Mausrades).

Es gibt nun zwei Möglichkeiten: Entweder der in der Zeile enthaltene Code ist schon so weit optimiert, dass sich die Länge nicht mehr vermindern lässt – dann brechen Sie die Zeile einfach um, indem Sie am Ende des ersten Teils ein Leerzeichen und einen Unterstrich anfügen und den Rest in die folgende Zeile verbannen. Aus der Zeile
Set objAccess = CurrentProject.AllForms(strName)
entsteht dann beispielsweise die folgende Zeile:
Set objAccess = _
CurrentProject.AllForms(strName)

Der Zeilenumbruch kann an jeder Stelle außer innerhalb eines Ausdrucks erfolgen. Eine weitere mögliche Variante wäre folgende:
Set objAccess = CurrentProject. _
AllForms(strName)

Nicht gültig wäre diese Alternative:
Set objAccess = Current _
Project.AllForms(strName)

Etwas komplizierter wird es, wenn Sie den Zeilenumbruch innerhalb einer in Anführungszeichen befindlichen Zeichenkette einfügen möchten. In dem Fall teilen Sie die Zeichenkette zunächst an der gewünschten Stelle auf, indem Sie zwei in Anführungszeichen und durch ein Kaufmanns-Und (&) getrennte Teilzeichenketten erzeugen. Dieses Zeichen wird übrigens im englischspachigen Raum auch als Ampersand bezeichnet. Dann können Sie den Zeilenumbruch wie oben vor oder hinter dem Kaufmanns-Und vornehmen. So wird aus der Zeile
MsgBox "Beispiel für einen Zeilenumbruch"
dieser Zweizeiler (Einrückung nicht vergessen!):
MsgBox "Beispiel für " _
& "einen Zeilenumbruch"

Möglicherweise können Sie die Zeile auch auf ganz andere Weise klein kriegen. Bei den vorangegangenen Beispielen wurden bewusst kurze Zeilen gewählt, damit die Ausgangszeilen auch in einer Zeile abgedruckt werden können. Im wirklichen Leben hat man es aber beispielsweise mit endlosen Verkettungen von Bedingungen etwa in einer If-Then-Bedingung zu tun. Die Übersicht leidet dann nicht nur, weil die Zeile nicht auf der gegebenen Bildschirmbreite angezeigt werden kann, sondern auch, weil man unter Umständen Minuten braucht, um den Ausdruck zu analysieren.

Kommentare

Wenn Sie Kommentare in Ihrem Code verwenden möchten, gibt es zwei Möglichkeiten: Sie schreiben den Kommentar über die betroffene Zeile(n) oder dahinter.

Generell sollte der Code allerdings so gut lesbar sein, dass Sie keine Kommentare benötigen. Wenn Sie Routinennamen, Variablenbezeichnungen und Objektnamen sinnvoll wählen, gibt es vermutlich nur noch wenige Gründe für das Kommentieren von Code. Wenn Sie also beispielsweise eine Variable noch erläutern müssen, sollten Sie darüber nachdenken, den Variablennamen so anzupassen, dass er selbsterklärend ist.

Einsatz von Variablen

Der richtige Einsatz von Variablen spielt eine große Rolle bei der Codequalität. Folgendes sollten Sie dabei beachten:

Bezeichnungen von Variablen

Wie bereits weiter oben erwähnt, sollten Sie bei der Benennung von Variablen die Ungarische Notation verwenden. Je nach Variablentyp stellen Sie dabei ein entsprechendes Präfix voran und hängen in manchen Fällen ein Suffix an (wie etwa Min oder Max).

Der Rest des Variablennamens besteht in vielen Fällen aus einer einfachen Bezeichnung wie rstKunden, strKundenname oder lngTitelID.

Für alles, was zwischen Präfix und Suffix liegt und aus mehr als einem Wort besteht, legen Sie eine Regel fest. In der Praxis hat es sich bewährt, Zusätze zur eigentlichen Bezeichnung vor die Bezeichnung zu stellen. Beispiele dafür sind datAktuellesDatum oder intLaufendeNummer.

Hier ist bereits zu erkennen, dass man neue Wörter innerhalb des Variablennamens groß schreibt. Verfallen Sie nicht in die Unart, dies auch innerhalb von zusammengesetzten Wörtern zu machen – also bitte kein datMindest-HaltbarkeitsDatum oder ähnliche Konstrukte.

Einsatz von Routinen

Routinen dienen dem Gliedern der Funktionalität einer Anwendung. Während vor langer Zeit ein Basic-Programm aus einer Datei bestand, deren Ablauf durch Zeilennummern und die passenden Goto-Anweisungen sowie einigen weiteren Steuerstrukturen festgelegt war, ist heute die Verwendung von Sub- und Function-Prozeduren weit verbreitet.

Das werden die meisten von Ihnen vermutlich wissen, aber die Praxis zeigt, dass viele Anwendungen scheitern, weil kein ausreichender Gebrauch von solchen Routinen gemacht wird.

Die wichtigste Frage, die es in diesem Zusammenhang zu beantworten gilt, ist folgende: Wann sollte man eine oder mehrere Zeilen Code in einer eigenen Routine unterbringen? Es gibt mindestens zwei Anhaltspunkte, wann eine eigene Routine angezeigt ist (es gibt sicher noch mehr, aber diese hier sind die wichtigsten):

Das Erzeugen von neuen Routinen aus bestehendem Code wird in der Refactoring-Maßnahme „Methode extrahieren“, die Sie im folgenden Teil dieser Beitragsreihe finden, genau beschrieben.

Wenn Sie Ihre Routinen so weit auseinander genommen haben, dass jede Routine nur noch eine Funktion erfüllt, haben Sie das erreicht, was im Allgemeinen „Starker Zusammenhalt“ genannt wird.

Ein weiteres Ziel ist, dass die Abhängigkeit zwischen Routinen möglichst minimiert und damit eine „lose Kopplung“ erreicht wird. Das bedeutet, dass der Aufruf einer Routine von einer anderen Routine aus möglichst einfach sein, also möglichst wenig Parameter benötigen soll. Wenn alle Parameter über den Aufruf übergeben werden, können Sie gleichzeitig gut erkennen, welche Informationen die aufgerufene Routine erwartet. Anders sieht es aus, wenn beide Routinen etwa eine gemeinsame globale Variable verwenden. Wenn Sie dann eine neue Routine schreiben, die eine Routine aufruft, für deren Benutzung zuvor eine globale Variable gefüllt werden muss, stimmt etwas nicht mit dem Entwurf der Anwendung.

Auch sollte der Aufruf einer Routine erfolgen können, ohne dass man Kenntnisse über den Ablauf innerhalb der aufzurufenden Routine hat.

Bezeichnungen von Routinen

Mit Routinen sind nachfolgend benutzerdefinierte Sub-Prozeduren und Function-Prozeduren gemeint. Der Name von Ereignisprozeduren und Property-Prozeduren wird vom jeweiligen Objektnamen oder Eigenschaftsnamen abgeleitet, hier müssen Sie also keinen Hirnschmalz hineinstecken.

Das Bezeichnen einer Routine und die Erkenntnis, ob die Routine eine gute Routine ist, liegen eng beieinander: Eine Routine sollte immer einen Namen erhalten, der die Funktion der Routine beschreibt. Wenn es Ihnen nicht gelingt, einen einprägsamen Namen für die Routine zu finden, enthält die Routine möglicherweise mehr Funktion, als sie eigentlich enthalten sollte. In dem Fall ist es an der Zeit, die enthaltene Funktionalität auf zwei oder mehr Funktionen aufzuteilen.

Zusammenfassung

Der erste Teil dieser Betragsreihe wirft Fragen auf, aus deren Antworten Sie einiges über die Qualität Ihres Codes ablesen können. Außerdem finden Sie hier einige Vorschläge, die Sie direkt beim Erstellen von Code berücksichtigen können, um gute Voraussetzungen für eine gute Lesbarkeit und Wartbarkeit des Codes zu erzielen.

Wie geht es weiter?

Bis jetzt habe ich bewusst auf ausführliche Beispiele zu den Vorschlägen verzichtet, da dies sonst leicht das ganze Magazin gefüllt hätte. Dafür gibt es in den folgenden beiden Teilen umso mehr praktische Anleitungen. Dort stellt Expert´s inside Access Ihnen Möglichkeiten vor, wie Sie bestehenden Code mit verschiedenen Refactoring-Maßnahmen optimieren können. Auch das eine oder andere Thema aus dem ersten Teil werden wir noch einmal aufgreifen und mit den notwendigen Beispielen unterfüttern.