2010-12-23 12 views
5

Ich brauche manchmal Klassen, die nur einmal während des Lebenszyklus der Anwendung instanziiert werden sollten. Das Erstellen von Singletons ist schlecht, weil dann das Komponententesten problematisch wird.Wie wird ein Beinahe-Singleton implementiert?

Aber da es eine einzige Instanz solcher Objekte während des Lebenszyklus meiner Anwendung geben sollte, wäre es ein Fehler, ein solches Objekt zweimal zu instanziieren , wenn die Anwendung ausgeführt wird.

Daher würde ich meine App gefällt, sobald eine Ausnahme zu werfen, wie es während seines Lebenszyklus feststellt, dass ein solches Objekt instanziiert wird zweimal während noch mehrere Instanziierung eines solchen Objekts, während Unit-Tests ermöglicht.

Ich denke, es ist keine unzumutbare Anforderung: Wenn während eines Lebenszyklus der Anwendung nur ein solches Objekt erstellt werden sollte, scheint es eine gute Sache zu sein, eine Ausnahme zu werfen.

Hier ist, was ich tue:

/** 
* The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 
* "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 
* document are to be interpreted as described in RFC 2119. 
* 
* You MUST NOT instantiate this class more than once during the application's 
* lifecycle. If you try to do so, an exception SHALL be thrown. 
* 
* You SHOULD be able to instantiate this class more than once when unit 
* testing. 
* 
* 
*/ 
public class LifeCycle { 

    private static final AtomicInteger cnt = new AtomicInteger(0); 

    @NotNull 
    public static LifeCycle getNewLifeCycle() { 
     if (cnt.incrementAndGet() > 1 && App.isRealApp()) { 
      throw new IllegalStateException("Class is already instantiated"); 
     } 
     return new LifeCycle(); 
    } 

} 

wo App.isRealApp() wird immer falsche zurück, wenn ich Unit-Tests sind und immer wahr, wenn die reale App läuft.

Meine Frage ist einfach: macht es Sinn und wie soll ich das umsetzen?

Antwort

9

Wenn Ihr Entwurf ein Singleton erfordert, dann sind Sie am besten mit einem Singleton, anstatt etwas Kompliziertes zu versuchen.

Wenn Ihre Probleme beim Komponententest von Singletons eine Instanz zum Beispiel bekommen, dann würde ich einen instanziierbaren Lightweight Proxy erstellen, der dieselbe Schnittstelle wie Ihr Singleton bietet.

Der Proxy sollte keine Logik haben - er sollte nur Anrufe zum Singleton mappen.

Sie können es dann in Tests verwenden und die Codebasis intakt lassen. Der Lightweight-Proxy kann Teil der Testsuite bleiben und nicht freigegeben werden.

+1

+1, Ich mag die leichte Proxy-Idee! – NoozNooz42

+0

Es hat für mich funktioniert - und ich konnte die Codebasis so halten, wie sie war. –

5

Eine Lösung besteht darin, ein Abhängigkeitsinjektions-Framework wie Spring zu verwenden.

Die Anwendungskonfiguration würde die Singleton-Instanzen angeben, aber Ihre Testfälle könnten nach Bedarf Instanzen erstellen und sie manuell einspeisen.

Für weitere Informationen: http://static.springsource.org/spring/docs/2.5.x/reference/testing.html

Eine Alternative, wenn Sie bereits statische Referenzen Singleton im gesamten Code gestreut haben, ist Ihre Singleton.getInstance() Methode in eine wahre Fabrik zu konvertieren, eine Systemeigenschaft mit ihrem Verhalten zu steuern. Diese Systemeigenschaft könnte so einfach sein wie ein Flag, um anzuzeigen, dass der Test in Bearbeitung ist, oder so komplex wie der Name einer anderen Klasse, die als echte Factory verwendet wird (ähnlich wie das JDK mit DocumentBuilderFactory).

+0

@Anon: +1 too ... Ich finde es sehr interessant, dass Spring auf der Anwendungsebene konfiguriert werden kann, um nur ein Singleton zu erlauben, während das Testen von Einheiten noch erlaubt ist. Cool, ich werde es lesen. – NoozNooz42

+0

Ist ein komplexes Framework wie Spring für ein so einfaches Problem nicht zu komplex? Ich denke, die Antwort ist ziemlich klar. Sie sollten Ihren Testcode schreiben, damit Sie den Code testen und den Code nicht ändern können, um ihn testbar zu machen. – RoflcoptrException

+0

Spring könnte ein bisschen viel sein, aber eine Factory, die die Erstellung von Instanzen steuert, ist im Prinzip das Gleiche für diesen Zweck und viel leichter, solange die Refactoring-Last nicht zu hoch ist. –

0

Daher würde ich meine App gefällt, sobald eine Ausnahme zu werfen, wie es während seines Lebenszyklus feststellt, dass ein solches Objekt instanziiert wird zweimal

Das ist, was die Singleton erzwingt.

, während immer noch mehrere Instanziierung eines solchen Objekts während des Komponententests möglich ist.

Sie können dies mit @com.dp4j.Singleton

machen erzwingen Das ist, was die Reflection-API ermöglicht.

Sie können entweder die Reflection API direkt verwenden oder dp4j für Sie injizieren. Here finden Sie Code für beide.

Verwandte Themen