Die Programmiersprache VBA wird zwar von Microsoft selbst als objektorientiert bezeichnet, unterstützt aber bei weitem nicht alle Aspekte der Objektorientierung. Das Singleton Pattern ist eines der wenigen Pattern, die sich in VBA umsetzen lassen. Dieser Beitrag soll den Praxiseinsatz des Singletonpatterns in meinem VBA-Projekt etwas näher erläutern.
Ausgangssituation
In einem meiner größeren Kundenprojekte geht es um die Verarbeitung von Personendaten, die aus einem bunten Strauß unterschiedlicher strukturierten und semistukturierten Datenquellen (csv, xslx, xml, con, txt) importiert werden. Jede ausgelesene Person wird für die Weiterverarbeitung in einem Objekt der Personenklasse abgelegt und erhält anhand des ausgelesenen Institutionskennzeichens einige Zusatzparameter, die aus einem mittlerweile relativ großen Schlüsselverzeichnis stammen.
Zu Beginn des Projektes war die zu verarbeitende Menge an Personen relativ klein, sodass es nicht wirklich ins Gewicht gefallen ist, dass für jede Person das Schlüsselverzeichnis jedes Mal gelesen wurde. Als die zu verarbeitende Datensatzmenge immer weiter stieg, spiegelte sich dies deutlich in der Laufzeit wider. Ab einem gewissen Punkt war klar, dass es hier eine bessere Lösung her musste.
Lösungsansatz
Eine bessere Lösung heißt in diesem Fall, dass das Schlüsselverzeichnis ein einziges Mal von der Festplatte gelesen und während der gesamten Programmlaufzeit im Programmspeicher gehalten werden soll. Denn zum einen ist die Zugriffszeit auf ein internes Objekt ist um Längen besser, als die Zugriffszeit auf die Festplatte. Zum anderen bleibt das Schlüsselverzeichnis während der Programmlaufzeit konstant und ist somit für jede eingelesene Person identisch.
Um dies zu erreichen, hatte ich zwei mögliche Alternativen. Beide sind performancetechnisch nahezu identisch, unterscheiden sich lediglich in deren Umsetzung und Architektur. Wie der Beitragstitel bereits gespoilert hat, habe ich mich für die Variante 2 entschieden.
Variante 1 – Konfigobjekt wird von Außen mitgegeben
Bei dieser Variante wird vor dem Datenimport das Schlüsselverzeichnis durch das Erzeugen eines Objektes der Klasse Config gelesen werden. Dieses Objekt wird während der Verarbeitung jedem Personenobjekt im Konstruktor mitgegeben. Dadurch kann jedes Personenobjekt mit Hilfe des ausgelesenen Institutionskennzeichens und des Schlüsselverzeichnisobjektes den passenden Klartext und alle anderen notwendigen Parametern ermitteln.
Variante 2 – Einsatz des Singletonpatterns
Diese Variante verfolgt einen etwas anderen Ansatz. Dabei verbleibt des Erzeugen des Objektes der Schlüsselverzeichnis-Klasse Config in der Personenklasse. Diese wird dabei nach dem Singletonpattern aufgebaut und sorgt programmtechnisch dafür, dass trotz tausendfacher Aufrufe das Schlüsselverzeichnis nur ein einziges Mal physisch gelesen wird.
Schauen wir uns das ganze auf der Codeebene an. Dass die Objekterzeugung von ausgelagerten VBA-Klassen anders funktioniert als in herkömmlichen objektorientierten Sprachen, habe ich schon ausführlich in diesem Beitrag über AddIn-Programmierung beschrieben. Im Prinzip müssen wir den dort vorgestellten Code nur um wenige Anweisungen erweitern.
Die Hauptrolle bei diesem Pattern spielt die Objektdeklaration als Static. Dies bewirkt, dass das Objekt seine Gültigkeit auch dann beibehält, wenn die abgebildete Funktion vollständig durchgelaufen ist.
Public Function getKlasseninstanz() As Config
Static objConfig As Config
If objConfig Is Nothing Then
Set objConfig = New Config()
End If
Set getKlasseninstanz = objConfig
End Function
Wenn diese Methode zum ersten Mal aufgerufen wird (nämlich dann, wenn das erste Personenobjekt erzeugt wird), dann existiert das Objekt der Klasse Config noch nicht- dieses wird dann mit der Anweisung = new Config() zum ersten Mal erzeugt und anschließend an das Personenobjekt zurückgegeben.
Wird diese Methode bei allen darauf folgenden Personenobjekten erneut aufgerufen, dann stellt sie fest, dass es das Objekt bereits gibt (weil es ja durch die Static-Deklaration seine Gültigkeit nicht verloren hat) und gibt dieses Objekt (welches wir beim ersten Aufruf erzeugt haben) zurück, aus dem dann alle benötigten Parameter ausgelesen werden, ohne dass es eines physischen Lesens der Konfigurationsdatei bedarf.
Genau so funktioniert das Pattern auch mit herkömmlichen Objektorientierten Sprachen wie Java und C#. Lediglich ist dabei noch daran zu denken, dass der Konstruktor der Klasse auf Sichtbarkeit privat einzustellen ist, um zu unterbinden, dass weitere Objekte außerhalb der getKlasseninstanz-Methode erzeugt werden können. Bei VBA-Klassen (die in separate AddIns ausgelagert sind) ist es allerdings nicht erforderlich, weil sie sowieso nur über dieses Hilfskonstrukt mit der getKlasseninstanz-Methode instanziiert werden können.
Fazit
Der hier vorgestellte Ansatz hat in meinem Projekt zu einer deutlichen Einsparung der Laufzeit (ca. Faktor 2) geführt und war zudem wirklich sehr einfach umzusetzen. Mittlerweile setze ich das Singletonpattern in nahezu jedem Projekt ein, in dem es ums einmalige Lesen von Konfigurationsdateien geht.