Ist DI die einzige Lösung für Singleton und / oder statische Objekte?

8

Mir wurde gesagt, dass Singletons schwer zu testen sind.

Mir wurde gesagt, dass statische Methoden / Objekte auch nicht gut sind.

Im Grunde scheint die einzige Lösung DependencyInjection zu sein.

Aber ... ich kann mich wirklich nicht an DI gewöhnen, nimm dieses Beispiel:

In meinem Framework habe ich eine Klasse, die SQL verwaltet. Diese Klasse (und viele andere meines Frameworks) verwendet einen Singleton-Logger, um Nachrichten (und viele andere Helfer) zu protokollieren.

Mit DI würde sich mein Code zu:

drehen %Vor%

Das scheint jetzt nicht so schlimm zu sein. Aber denken Sie an eine Seite, die viele Abfragen benötigt. Ich glaube, es ist ziemlich überflüssig, jedesmal $logger im Konstruktor schreiben zu müssen, besonders wenn Sie bedenken, wenn PreparedQuery viele andere Abhängigkeiten im Konstruktor benötigt.

Die einzige Lösung für vermeiden Singleton, die ich gefunden habe, ist die Verwendung einer Methode (oder nur einer einfachen Funktion) in der Hauptanwendung, die alle Referenzen auf diese Hilfsobjekte speichert (Service Locator / Container), aber das löst nicht das Problem, die Abhängigkeiten zu verbergen

Also in Ihrer Erfahrung außer DI was ist ein gutes Muster zu verwenden?

Lösung:

Für alle Interessierten erklärt der Ersteller von PHPunit, wie man das Singleton-Problem lösen kann (und wie man das statische Testverfahren mit PHP 5.3 löst)

Ziemlich interessant zu lesen, wenn Sie mich fragen.

    
dynamic 16.05.2011, 21:38
quelle

5 Antworten

1

Die Protokollierung ist normalerweise das Beispiel, in dem statische Singletons in Ordnung sind. Sie müssen Ihr Logging sowieso nicht verspotten, oder?

    
erikkallen 16.05.2011, 21:42
quelle
3

Bitte verwenden Sie nicht global .
Sie müssen $ logger in Konstruktoren übergeben oder stattdessen den Service Container übergeben (auch bekannt als Objects Manager, Service Locator, Resources Manager).
Variante aus dem Symfony-Framework Ссылка
Sie können Ihren eigenen Objektmanager erstellen, und seine Methoden sollten nicht statisch sein.

    
OZ_ 16.05.2011 21:46
quelle
3

Nun, in diesem Fall würde ich stattdessen einen Builder (oder eine Factory) erstellen. Also würde deine Fabrik die Abhängigkeit für dich injizieren. Auf diese Weise können Sie auch Ihre Globals vermeiden:

%Vor%

So machen Sie das einmal:

%Vor%

Wenn Sie eine neue Abfrage benötigen, rufen Sie einfach an:

%Vor%

Das ist ein sehr einfaches Beispiel. Aber Sie können alle möglichen komplexen Logik hinzufügen, wenn Sie brauchen. Aber der Punkt ist, indem Sie new in Ihrem Code vermeiden, vermeiden Sie auch, die Abhängigkeiten zu verwalten. Stattdessen können Sie die Fabrik (en) nach Bedarf passieren.

Der Vorteil ist, dass dies alles zu 100% testbar ist, da alles überall injiziert wird (im Gegensatz zur Verwendung von Globals).

Sie können auch eine Registrierung verwenden (auch bekannt als Service Container oder DI-Container), aber stellen Sie sicher, dass Sie die Registrierung injizieren.

    
ircmaxell 16.05.2011 22:01
quelle
1

Die obigen Antworten geben Ihnen einige Ideen. Ich werde einen anderen vorstellen: Implementiere eine Plugin-Architektur. Der Logger wird zu einem Plugin, das Sie jederzeit aktivieren / deaktivieren / ändern können.

Ein vereinfachtes Beispiel:

%Vor%

Dadurch wird der Logger vom Konstruktor entfernt. Das ist es, was ich bevorzuge, wenn es nicht wichtig für das Funktionieren des Objekts ist.

    
koen 15.04.2012 10:36
quelle
0

Nach meinem Verständnis der Gespräche von Misko Hevery über DI und den Operator new ist das Problem, dass Sie weit genug bei der Implementierung von DI nicht gegangen sind.

Was er immer sagt, ist, dass Sie Geschäftslogik nicht mit Objektkonstruktion mischen sollten. In den beiden Zeilen Ihres Beispiels erstellt jedoch die erste ( $query = new PreparedQuery($logger); ) ein Objekt und dann die zweite ( $query->prepare(/* ... */); ) Geschäftslogik.

Offensichtlich ist das Ziel dieses Codes, eine Abfrage vorzubereiten, und anstatt sich Sorgen darüber zu machen, wie man ein PreparedQuery erstellt, sollte man im Klassenkonstruktor nur nach einem solchen fragen. Oder wenn es in der Lage sein muss, viele PreparedQueries zu produzieren, sollte es nach einem Prototyp (der es klont, wenn es einen neuen braucht) oder nach einem Fabrikobjekt fragen. Der Punkt ist, die Tatsache, dass die PreparedQuery einen Logger hat, spielt keine Rolle und sollte woanders erledigt werden.

Das Prinzip, im Konstruktor nach dem zu fragen, was man braucht, ist im Prinzip leicht zu verstehen, obwohl ich immer noch versuche, für mich selbst zu ergründen, was es in verschiedenen Situationen bedeutet und wie man es umsetzen kann nach oben (die "Hauptmethode" oder gleichwertig). Ich glaube jedoch, dass dieses Prinzip auf das allgemeine Problem anspricht, das Sie haben. Dieser new -Operator sollte nicht dort sein, wo er ist.

    
LinusR 25.03.2012 18:27
quelle

Tags und Links