Wir alle wissen, wie schlecht Singletons sind, weil sie Abhängigkeiten verbergen und aus anderen Gründen .
In einem Framework kann es jedoch viele Objekte geben, die nur einmal instanziiert und von überall aufgerufen werden müssen (Logger, Datenbank usw.).
Um dieses Problem zu lösen, wurde mir gesagt, dass ich einen sogenannten "Objects Manager" (oder Service Container wie Symfony) verwenden soll, der jeden Verweis auf Services (Logger usw.) intern speichert.
Aber warum ist ein Dienstleister nicht so schlecht wie ein reiner Singleton?
Der Dienstanbieter verbirgt auch Abhängigkeiten und schließt nur die Erstellung der ersten Beziehung ab. Ich habe wirklich Probleme zu verstehen, warum wir einen Dienstanbieter anstelle von Singletons verwenden sollten.
PS. Ich weiß, dass ich DI verwenden sollte, um Abhängigkeiten nicht zu verbergen (wie von Misko angegeben).
Hinzufügen
Ich würde hinzufügen: Heutzutage sind Singletons nicht so böse, der Schöpfer von PHPUnit hat es hier erklärt:
DI + Singleton löst das Problem:
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
Das ist ziemlich klug, auch wenn dies nicht alle Probleme löst.
Gibt es außer DI und Service Container eine gute akzeptable Lösung für den Zugriff auf diese Hilfsobjekte?
quelle
Antworten:
Service Locator ist sozusagen nur das kleinere von zwei Übeln. Das "Geringere", das auf diese vier Unterschiede hinausläuft ( zumindest kann ich mir momentan keine anderen vorstellen ):
Prinzip der Einzelverantwortung
Service Container verstößt nicht wie Singleton gegen das Prinzip der Einzelverantwortung. Singletons kombinieren Objekterstellung und Geschäftslogik, während der Service Container streng für die Verwaltung der Objektlebenszyklen Ihrer Anwendung verantwortlich ist. In dieser Hinsicht ist Service Container besser.
Kupplung
Singletons werden aufgrund der statischen Methodenaufrufe normalerweise fest in Ihre Anwendung codiert, was zu eng gekoppelten und schwer zu verspottenden Abhängigkeiten führt in Ihrem Code führt. Der SL hingegen ist nur eine Klasse und kann injiziert werden. Während also alle Ihre Klassen davon abhängen, handelt es sich zumindest um eine lose gekoppelte Abhängigkeit. Wenn Sie den ServiceLocator nicht als Singleton selbst implementiert haben, ist dies etwas besser und auch einfacher zu testen.
Alle Klassen, die den ServiceLocator verwenden, hängen jetzt jedoch vom ServiceLocator ab, der auch eine Form der Kopplung darstellt. Dies kann durch die Verwendung einer Schnittstelle für den ServiceLocator verringert werden, sodass Sie nicht an eine konkrete ServiceLocator-Implementierung gebunden sind. Ihre Klassen hängen jedoch von der Existenz eines Locators ab, während die Nichtverwendung eines ServiceLocator die Wiederverwendung erheblich erhöht.
Versteckte Abhängigkeiten
Das Problem, Abhängigkeiten zu verbergen, besteht jedoch sehr. Wenn Sie den Locator nur in Ihre konsumierenden Klassen einfügen, kennen Sie keine Abhängigkeiten. Im Gegensatz zum Singleton instanziiert der SL normalerweise alle Abhängigkeiten, die hinter den Kulissen benötigt werden. Wenn Sie also einen Service abrufen, werden Sie nicht so abrufen Misko Hevery im CreditCard-Beispiel enden , z. B. müssen Sie nicht alle Abhängigkeiten der Abhängigkeiten von Hand instanziieren.
Das Abrufen der Abhängigkeiten aus der Instanz heraus verletzt ebenfalls Demeter-Gesetz , das besagt, dass Sie sich nicht mit Mitarbeitern befassen sollten. Eine Instanz sollte nur mit ihren unmittelbaren Mitarbeitern sprechen. Dies ist sowohl bei Singleton als auch bei ServiceLocator ein Problem.
Globaler Staat
Das Problem des globalen Status wird auch etwas gemildert, da beim Instanziieren eines neuen Service Locator zwischen den Tests auch alle zuvor erstellten Instanzen gelöscht werden (es sei denn, Sie haben den Fehler gemacht und sie in statischen Attributen im SL gespeichert). Das gilt natürlich nicht für einen globalen Zustand in Klassen, die von der SL verwaltet werden.
Weitere Informationen finden Sie unter Fowler zu Service Locator und Abhängigkeitsinjektion .
Ein Hinweis zu Ihrem Update und der verlinkte Artikel von Sebastian Bergmann zum Testen von Code, der Singletons verwendet : Sebastian legt in keiner Weise nahe, dass die vorgeschlagene Problemumgehung die Verwendung von Singleons weniger problematisch macht. Es ist nur eine Möglichkeit, Code zu erstellen, der sonst nicht testbarer wäre. Aber es ist immer noch problematischer Code. In der Tat bemerkt er ausdrücklich: "Nur weil Sie können, heißt das nicht, dass Sie sollten".
quelle
Das Service Locator-Muster ist ein Anti-Pattern. Das Problem des Offenlegens von Abhängigkeiten wird dadurch nicht gelöst (anhand der Definition einer Klasse können Sie nicht erkennen, um welche Abhängigkeiten es sich handelt, da sie nicht injiziert, sondern aus dem Service Locator herausgerissen werden).
Ihre Frage lautet also: Warum sind Service Locators gut? Meine Antwort lautet: Sie sind es nicht.
Vermeiden, vermeiden, vermeiden.
quelle
Der Service-Container verbirgt Abhängigkeiten wie das Singleton-Muster. Vielleicht möchten Sie stattdessen die Verwendung von Abhängigkeitsinjektionscontainern vorschlagen, da diese alle Vorteile von Servicecontainern bietet, jedoch (soweit ich weiß) keine Nachteile von Servicecontainern aufweist.
Soweit ich weiß, besteht der einzige Unterschied zwischen beiden darin, dass im Servicecontainer der Servicecontainer das Objekt ist, das injiziert wird (wodurch Abhängigkeiten ausgeblendet werden). Wenn Sie DIC verwenden, injiziert der DIC die entsprechenden Abhängigkeiten für Sie. Die Klasse, die vom DIC verwaltet wird, ist sich der Tatsache, dass sie von einem DIC verwaltet wird, völlig bewusst, sodass Sie weniger Kopplung, klare Abhängigkeiten und glückliche Komponententests haben.
Dies ist eine gute Frage bei SO, die den Unterschied zwischen beiden erklärt: Was ist der Unterschied zwischen den Mustern Dependency Injection und Service Locator?
quelle
Da Sie Objekte im Service Container problemlos durch
1) Vererbung ersetzen können (Object Manager-Klasse kann vererbt und Methoden überschrieben werden)
2) Konfiguration ändern (im Fall von Symfony)
Und Singletons sind nicht nur wegen der hohen Kopplung schlecht, sondern auch, weil sie einzelne Tonnen sind. Es ist eine falsche Architektur für fast alle Arten von Objekten.
Mit 'pure' DI (in Konstruktoren) zahlen Sie einen sehr hohen Preis - alle Objekte sollten erstellt werden, bevor sie im Konstruktor übergeben werden. Dies bedeutet mehr genutzten Speicher und weniger Leistung. Außerdem kann nicht immer nur ein Objekt erstellt und im Konstruktor übergeben werden - es kann eine Abhängigkeitskette erstellt werden ... Mein Englisch ist nicht gut genug, um dies vollständig zu diskutieren. Lesen Sie es in der Symfony-Dokumentation.
quelle
Ich versuche aus einem einfachen Grund, globale Konstanten und Singletons zu vermeiden. Es gibt Fälle, in denen möglicherweise APIs ausgeführt werden müssen.
Zum Beispiel habe ich Front-End und Admin. Innerhalb von admin möchte ich, dass sie sich als Benutzer anmelden können. Betrachten Sie den Code in admin.
Dies kann eine neue Datenbankverbindung, einen neuen Logger usw. für die Frontend-Initialisierung herstellen und prüfen, ob der Benutzer tatsächlich vorhanden, gültig usw. ist. Außerdem werden geeignete separate Cookie- und Ortungsdienste verwendet.
Meine Idee von Singleton ist: Sie können nicht zweimal dasselbe Objekt innerhalb des übergeordneten Objekts hinzufügen. Zum Beispiel
würde Sie mit einer einzelnen Instanz und beiden Variablen verlassen, die darauf zeigen.
Wenn Sie eine objektorientierte Entwicklung verwenden möchten, arbeiten Sie mit Objekten und nicht mit Klassen.
quelle
$api
Variable um Ihr Framework herum zu übergeben? Ich habe nicht genau verstanden, was du meinst. Auch wenn der Aufrufadd('Logger')
dieselbe Instanz zurückgibt, haben Sie im Grunde einen Service-Cotainer