it-swarm.com.de

React Native - Ist die Verwendung eines Singletons die beste Alternative zu DI?

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 verarbeitet das Login (speichern Sie das Login-Token und senden Sie es bei jeder Anfrage nach dem Login).

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 benutze dieses Projekt, um so viel wie möglich zu lernen, damit ich hier das Beste tun kann. 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 zu sein, es in React Native) zu implementieren. Ich habe einige weitere Untersuchungen durchgeführt 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 Client-Implementierung REST) von meinen Komponenten entkoppelt.

8
Mateo Hrastnik

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, die RESTClient verwenden muss, um einige Daten abzurufen und dem Benutzer anzuzeigen. Der "Singleton" -Ansatz würde ungefähr so ​​aussehen:

import restClient from '...' // import singleton

class MyList extends React.Component {
  // use restClient and render stuff
}

Dies koppelt MyList eng an restClient, und es gibt keine Möglichkeit, den Unit-Test MyList durchzuführen, ohne restClient zu testen. Der DI-Ansatz würde ungefähr so ​​aussehen:

function MyListFactory(restClient) {
  class MyList extends React.Component {
    // use restClient and render stuff
  }

  return MyList
}

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, und ich ziehe es vor, diese Dinge nicht über React -Eigenschaften) weiterzugeben, da sie nicht portierbar sind und alle übergeordneten Komponenten müssen wissen, dass sie alle Requisiten an Kinder weitergeben müssen.

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 Komponente MyApp, die MyList verwendet. Der "Singleton" -Ansatz wäre:

import MyList from '...'
class MyApp extends React.Component {
  render() {
    return <MyList />
  }
}

Der DI-Ansatz lautet:

function MyAppFactory(ListComponent) {
  class MyApp extends React.Component {
    render() {
      return <ListComponent />
    }
  }

  return MyApp
}

Jetzt können wir MyApp testen, ohne MyList direkt zu testen. Wir könnten sogar MyApp mit einer völlig anderen Art von Liste wiederverwenden. Dieses Muster sprudelt bis zum Kompositionswurzel . Hier rufen Sie Ihre Fabriken an und verdrahten alle Komponenten.

import RESTClient from '...'
import MyListFactory from '...'
import MyAppFactory from '...'

const restClient = new RESTClient(...)
const MyList = MyListFactory(restClient)
const MyApp = MyAppFactory(MyList)

ReactDOM.render(<MyApp />, document.getElementById('app'))

Jetzt verwendet unser System eine einzelne Instanz von RESTClient, aber wir haben es so entworfen, dass Komponenten lose gekoppelt und einfach zu testen sind.

15
Samuel

Gemäß Ihren Prämissen (Lernen) lautet die einfachste Antwort Nein, Singletons sind nicht die beste Alternative zur Abhängigkeitsinjektion .

Wenn Lernen das Ziel ist, wird DI in Ihrer Toolbox eine wertvollere Ressource sein 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 Über den gesamten Code injizieren können. Sie werden feststellen, dass Ihr Code einfacher zu testen und lose gekoppelt ist. ( Weitere Details finden Sie unter 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.

Es ist jetzt, während des Trainings, wenn Sie Ihre Golden Hammers fälschen. Wenn Sie also das Erlernen von DI vermeiden möchten, ist es wahrscheinlich, dass Sie jedes Mal Singletons implementieren brauche DI.

5
Laiv

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, a zu ersetzen Singleton mit einer testspezifischen Implementierung, als dies bei Verwendung von DI der Fall wäre.

In diesem Fall empfehle ich , das Design zu verwenden, 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.)

0
Lutz Prechelt