Groovy und das Singleton-Pattern
Groovy tritt an, das Programmieren im Java-Umfeld einfacher und effizienter zu machen - und löst dieses Versprechen auch im Großen und Ganzen in beeindruckender Weise ein. Aber nicht alles wird einfacher durch Groovy, und manche gewohnten Lösungswege müssen erst einmal völlig neu durchdacht werden. Das weit verbreitete Singleton-Pattern ist hierfür ein Beispiel.
Einfache Singletons im Java-Stil
Wenn Sie ein einfaches Singleton haben wollen, also eine Klasse, von der es garantiert nur eine Instanz gibt, gehen Sie beispielsweise so vor: Sie
- verpassen der Klasse ein privates statisches Feld, das genau die eine Instanz hält,
- fügen eine öffentliche Methode hinzu, mit der man sich die singuläre Instanz holen kann und
- deklarieren den Konstruktor als privat, damit er nur aus der Klasse selbst heraus, nämlich zum Erzeugen der einen Instanz, aufgerufen werden kann.
Das Ganze sieht in Groovy so aus, wie das folgende Beispiel für eine Zähler-Klasse zeigt. Sie enthält eine Integer-Variable, die bei jedem Aufruf der Methode count um eins erhöht wird:
class SomeCounter { def count = 0 private static final theInstance = new SomeCounter() static getInstance(){ return theInstance } private SomeCounter() {} void count() { println "${this}: ${++count}" } }
Das funktioniert - in Groovy wie in Java - zunächst einmal ganz wunderbar. Die Ausgabe der Zählmethode zeigt uns, dass jedes Mal dieselbe Instanz aufgerufen wird und der Zähler auch immer hochzählt.
groovy> SomeCounter.instance.count() SomeCounter@d5c653: 1 groovy> SomeCounter.instance.count() SomeCounter@d5c653: 2 groovy> SomeCounter.instance.count() SomeCounter@d5c653: 3
Also alles OK? Mitnichten. Der private Konstruktor soll ja verhindern, dass ein Programmierer oder eine Programmiererin nichts Böses ahnend versehentlich die Singleton-Klasse direkt instanziiert. Aber Groovy kennt keine Privatsphäre bei Objekten (und bis zur Version 2.0 wird sich dies wohl kaum ändern). Checken wir dies:
groovy> new SomeCounter().count() SomeCounter@cfb11f: 1 groovy> new SomeCounter().count() SomeCounter@17577f9: 1
Da haben wir‘s. Nichts hindert Sie daran, neue Instanzen mit new zu erzeugen, anstatt den einen Zähler zu erhöhen (und nicht immer ist der Fehler so leicht zu erkennen wir hier). So geht es also nicht. Aber wie denn sonst?
Eine Verbesserung für Groovy
Eine einfache Gegenmaßnahme besteht darin, den Konstruktor sicherheitshalber prüfen zu lassen, ob bereits eine Instanz vorhanden ist, und gegebenenfalls eine Exception auslösen zu lassen.
private SomeCounter() { if (theInstance) throw new IllegalStateException("Singleton, verwende getInstance() statt new") }
Jetzt lässt es sich zwar immer noch nicht verhindern, dass jemand versucht, den Zähler zu instanziieren, aber zur Laufzeit gibt es eine unmissverständliche Fehlermeldung:
groovy> new SomeCounter().count() Exception thrown: java.lang.IllegalStateException: Singleton, verwende getInstance() statt new
Damit kann man durchaus schon einmal Leben. Dass Programmierfehler, die bei Java schon den Compiler protestieren lassen, bei Groovy erst zur Laufzeit erkennbar werden, sind wir wegen des dynamischen Charakters dieser Sprache gewohnt. Exzessive Unit-Tests - die zu verwenden ohnehin sinnvoll ist - gleichen diesen Mangel aus. Wichtig aber ist, dass der Fehler überhaupt erkennbar wird, und dafür sorgt hier die ‚‚IllegalStateException‘‘.
Etwas mehr Groovy bitte
Die Lösung funktioniert, ist aber nicht so richtig groovy. Viel Eleganter geht es, wenn wir die Möglichkeiten der Groovy-Metaprogrammierung nutzen. Mit recht einfachen Mitteln können wir erreichen, dass der Konstruktor-Aufruf keine neue Instanz anlegt, sondern einfach immer dasselbe Singleton-Objekt liefert. Dafür gibt es verschiedene Möglichkeiten, wir entscheiden uns für die Verwendung der ExpandoMetaClass. Sie ermöglicht es uns, die Modifikation direkt inline vorzunehmen und erspart uns das Anlegen einer eigenen Metaklasse. Die ganze SomeCounter-Klasse sieht dann beispielsweise so aus:
class SomeCounter { def count = 0 private static final theInstance = new SomeCounter() private SomeCounter() { SomeCounter.metaClass.constructor = {-> theInstance } } void count() { println "${this}: ${++count}" } }
Im Konstruktor lassen wir uns nun die Metaklasse zu SomeCounter geben und weisen ihr einen neuen argumentlosen Konstruktor in Form einer Closure zu, die nichts weiter tut, als unsere Singleton-Klasse zurückzugeben. Und siehe da…
groovy> new SomeCounter().count() SomeCounter@111c3f0: 1 groovy> new SomeCounter().count() SomeCounter@111c3f0: 2 groovy> new SomeCounter().count() SomeCounter@111c3f0: 3
Obwohl wir wiederholt new verwenden, bekommen wir immer dasselbe Objekt, und der Zähler wird brav hochgezählt.
Singletons für Groovy und Java
Für Groovy ist das mittels Metaprogrammierung implementierte Singleton perfekt. Aber was geschieht, wenn wir SomeCounter auch aus Java-Klassen heraus verwenden wollen? Ein Java-Programm kennt keine Groovy-Metaklassen und „sieht“ demzufolge nur den in der Klasse definierten „richtigen“ Konstruktor, der aber privat und daher für das Java-Programm tabu ist.
Damit man aus Java heraus überhaupt an die Singleton-Instanz herankommt, wird also die Methode getInstance() aus der ersten Version unseres SomeCounter benötigt. Fügen wir diesen ein, erhalten wir eine Singleton-Klasse, deren Instanz man aus Groovy sehr elegant über den Konstruktor und aus Java ganz traditionell über die getInstance()-Methode erhalten kann.
Allerdings könnte der unterschiedliche Zugriff auf das Singleton je nach verwendeter Programmiersprache für Verwirrung sorgen. Daher ist in gemischten Umgebungen wohl doch eher die traditionelle Variante mit der Verbesserung für Groovy zu empfehlen, bei ein versehentlicher Aufruf des Konstruktors aus Groovy heraus durch eine Runtime-Exception verhindert wird.
Die @Singleton-Annotation
Seit der Groovy-Version 1.6 gibt es eine eingebaute AST-Transformation, die genau dasselbe bewirkt wie das traditionelle Pattern mit dem abgefangenen Konstruktoraufruf (also unsere obige Empfehlung), allerdings muss man nun nichts mehr extra programmieren. Es genügt, der Klasse die Annotation @Singleton hinzuzufügen:
@Singleton class SomeCounter { def count = 0 void count() { println "${this}: ${++count}" } }
Eine statische Methode getInstance() und ein öffentlicher Konstruktor, der eine Laufzeit-Exception auslöst, wird dann automatisch hinzugefügt.

Diskussion