Ich habe viel über das Singleton-Muster gelesen und wie es "schlecht" ist, weil es die Klassen, die es verwenden, schwer zu testen macht, so dass es vermieden werden sollte. Ich habe einige Artikel gelesen, in denen erklärt wird, wie der Singleton durch Abhängigkeitsinjektion ersetzt werden kann, aber er erscheint mir unnötig komplex.
Hier ist mein Problem etwas detaillierter. Ich erstelle eine mobile App mit React Native und möchte einen REST-Client erstellen, der mit dem Server kommuniziert, Daten abruft, Daten veröffentlicht und die Anmeldung verarbeitet (speichern Sie das Anmeldetoken und senden Sie es bei jeder Anforderung nach der Anmeldung).
Mein ursprünglicher Plan war es, ein Singleton-Objekt (RESTClient) zu erstellen, mit dem sich meine App zunächst anmeldet und dann bei Bedarf die Anmeldeinformationen sendet. Der DI-Ansatz scheint mir wirklich kompliziert zu sein (vielleicht, weil ich DI noch nie zuvor verwendet habe), aber ich verwende dieses Projekt, um so viel wie möglich zu lernen, damit ich hier das Beste tun möchte. Anregungen und Kommentare werden sehr geschätzt.
Bearbeiten: Ich habe jetzt festgestellt, dass ich meine Frage schlecht formuliert habe. Ich wollte eine Anleitung, wie man das Singleton-Muster in RN vermeidet und sollte ich es überhaupt tun. Zum Glück gab mir Samuel genau die Antwort, die ich wollte. Mein Problem war, dass ich das Singleton-Muster vermeiden und DI verwenden wollte, aber es schien wirklich kompliziert, es in React Native zu implementieren. Ich habe weitere Nachforschungen angestellt und es mit dem Reacts-Kontextsystem implementiert.
Für alle Interessierten hier ist, wie ich es gemacht habe. Wie gesagt, ich habe den Kontext in RN verwendet, der so etwas wie Requisiten ist, aber auf jede Komponente übertragen wird.
In der Root-Komponente stelle ich die erforderlichen Abhängigkeiten wie folgt bereit:
export default class Root extends Component {
getChildContext() {
restClient: new MyRestClient();
}
render() {...}
}
Root.childContextTypes = {restClient: PropTypes.object};
Jetzt ist restClient in allen Komponenten unter Root verfügbar. Ich kann so darauf zugreifen.
export default class Child extends Component {
useRestClient() {
this.context.restClient.getData(...);
}
render() {...}
}
Child.contextTypes = {restClient: PropTypes.object}
Dadurch wird die Objekterstellung effektiv von der Logik entfernt und die REST-Client-Implementierung von meinen Komponenten entkoppelt.
quelle
Antworten:
Die Abhängigkeitsinjektion muss überhaupt nicht komplex sein und es lohnt sich absolut, sie zu lernen und anzuwenden. Normalerweise wird es durch die Verwendung von Abhängigkeitsinjektions-Frameworks kompliziert, aber sie sind nicht erforderlich.
In der einfachsten Form übergibt die Abhängigkeitsinjektion Abhängigkeiten, anstatt sie zu importieren oder zu konstruieren. Dies kann durch einfaches Verwenden eines Parameters für das, was importiert werden soll, implementiert werden. Nehmen wir an, Sie haben eine Komponente mit dem Namen
MyList
, mitRESTClient
der einige Daten abgerufen und dem Benutzer angezeigt werden müssen. Der "Singleton" -Ansatz würde ungefähr so aussehen:Diese eng Paare
MyList
zurestClient
, und es gibt keine Möglichkeit, Unit - Test kannMyList
ohne PrüfungrestClient
. Der DI-Ansatz würde ungefähr so aussehen:Das ist alles was man braucht um DI zu benutzen. Es werden höchstens zwei Codezeilen hinzugefügt, und Sie können einen Import eliminieren. Der Grund, warum ich eine neue Funktion "factory" eingeführt habe, ist, dass AFAIK Sie keine zusätzlichen Konstruktorparameter in React übergeben können. Ich ziehe es vor, diese Dinge nicht über die React-Eigenschaften weiterzugeben, da sie nicht portierbar sind und alle übergeordneten Komponenten wissen müssen, um alle zu übergeben Requisiten für Kinder.
Jetzt haben Sie eine Funktion zum Erstellen von
MyList
Komponenten, aber wie verwenden Sie sie? Das DI-Muster sprudelt die Abhängigkeitskette hoch. Angenommen, Sie haben eine KomponenteMyApp
, die verwendetMyList
. Der "Singleton" -Ansatz wäre:Der DI-Ansatz lautet:
Jetzt können wir testen,
MyApp
ohneMyList
direkt zu testen . Wir könnten sogarMyApp
mit einer völlig anderen Art von Liste wiederverwenden . Dieses Muster sprudelt bis zur Wurzel der Komposition . Hier rufen Sie Ihre Fabriken an und verdrahten alle Komponenten.Jetzt verwendet unser System eine einzelne Instanz von
RESTClient
, aber wir haben es so konzipiert, dass Komponenten lose gekoppelt und einfach zu testen sind.quelle
MyAppFactory
Sollte dieMyApp
Klasse auch in Ihrem Beispiel nicht zurückgegeben werden ?Gemäß Ihren Prämissen (Lernen) lautet die einfachste Antwort Nein. Singletons sind nicht die beste Alternative zur Abhängigkeitsinjektion .
Wenn Lernen das Ziel ist, werden Sie feststellen, dass DI eine wertvollere Ressource in Ihrer Toolbox ist als Singleton. Es mag komplex erscheinen, aber die Lernkurve basiert fast auf allem, was Sie von Grund auf lernen müssen. Legen Sie sich nicht auf Ihre Komfortzone, sonst wird überhaupt nicht gelernt.
Technisch gesehen gibt es kaum einen Unterschied zwischen Singleton und Einzelinstanz (was ich denke, dass Sie versuchen zu tun). Wenn Sie DI lernen, werden Sie feststellen, dass Sie einzelne Instanzen im gesamten Code einfügen können. Sie werden feststellen, dass Ihr Code einfacher zu testen und lose gekoppelt ist. ( Weitere Informationen finden Sie in Samuels Antwort. )
Aber hör nicht hier auf. Implementieren Sie denselben Code mit Singleton. Vergleichen Sie dann beide Ansätze.
Wenn Sie beide Implementierungen verstehen und mit ihnen vertraut sind, können Sie wissen, wann sie geeignet sind, und wahrscheinlich sind Sie in der Lage, die Frage selbst zu beantworten.
Während des Trainings schmieden Sie jetzt Ihre Goldenen Hämmer . Wenn Sie also das Erlernen von DI vermeiden möchten, werden Sie wahrscheinlich jedes Mal Singletons implementieren, wenn Sie DI benötigen.
quelle
Ich stimme den anderen Antworten zu, dass es eine gute Idee ist, zu lernen, was DI ist und wie man es verwendet.
Die Warnung, dass Singletons das Testen zu schwierig machen, wird normalerweise von Personen gemacht, die statisch typisierte Sprachen (C ++, C #, Java usw.) verwenden .
Im Gegensatz dazu ist es in einer dynamischen Sprache (Javascript, PHP, Python, Ruby usw.) normalerweise kaum schwieriger, einen Singleton durch eine testspezifische Implementierung zu ersetzen, als dies bei Verwendung von DI der Fall wäre.
In diesem Fall empfehle ich die Verwendung eines Designs, das sich für Sie und Ihre Mitentwickler natürlicher anfühlt , da dadurch Fehler vermieden werden. Wenn das zu Singletons führt, soll es so sein.
(Aber andererseits: Lernen Sie DI, bevor Sie diese Entscheidung treffen.)
quelle