Abhängigkeit Injekton oder Service Locator - Symfony

8

Ich habe angefangen, durch den Code von Symfony2 zu gehen, habe ein bisschen kleine Klassen wie Pimple studiert und bin nach einigen Stunden der Untersuchung zu einer seltsamen Idee gekommen. Der beste Anfang dafür ist zu erklären, wie ich wenige Begriffe verstehe, also:

Abhängigkeit Etwas, das für eine andere Sache benötigt wird, wie "Motor" in "Auto"

Container Objekt oder Klasse, die viele andere Objekte speichern kann, wie "Motor", "Getriebe" oder sogar "Auto"

Abhängigkeitsinjektion Prozess, bei dem jede Abhängigkeit in ein Objekt injiziert wird. Wenn ich also "Auto" brauche, weiß ich, dass ich "Motor", "Getriebe" und viele andere Dinge einspritzen muss. Wichtig ist, dass "Auto" nicht "Motor" erzeugt, sondern "Motor" in "Auto"

gestellt wird

Servicelokator Prozess, bei dem ein Objekt nach einem anderen Objekt fragt, zum Beispiel in das Auto, in das unser Container eingeführt wird, und wenn das Auto es starten muss, muss es vom Container "Motor" kommen, damit der Container ihn "Motor" zurückgibt

Als ich Symphony-Code studierte, beginnen sie mit Dependency-Injection, aber nach einiger Zeit merke ich, dass, wenn Controller erstellt wird, ganze Container injiziert werden und Sie dann $ this- & gt; get ('serviceName') erhalten können Es sieht also eher nach Service-Locator aus, was laut einigen Artikeln Anti-Pattern ist.

Wie ist es? Ist diese Linie zwischen DI und SL so klein, dass sie manchmal kaputt ist? Oder habe ich etwas falsch verstanden? Wenn ich DI verwende, muss ich jeden Dienst in den Controller einfügen, also weiß ich von außen, was ich benutze? Oder kann Controller in manchen Fällen Container werden?

    
M.Svrcek 10.10.2014, 17:19
quelle

1 Antwort

9

Dein Verständnis von DI ist ziemlich gut. Und ja, Symfony Controller implementiert ContainerAwareInterface , und wie Sie gesagt haben, hat eine Service-Locator-Rolle. Service Locator ist jedoch kein Anti-Pattern. Jedes Muster hat seine richtige und unpassende Verwendung.

Außerdem erzwingt Symfony Sie in keiner Weise, seinen Controller zu verwenden. Ihr Controller kann ein Service sein . Hölle, es kann sogar eine Funktion sein!

Dies ist einer der Gründe, warum Controller als Service-Locators implementiert werden: Leistung .

Lassen Sie uns die Autoanalogie fallen lassen und uns auf den wahren Fall konzentrieren, den Sie in 99% der Projekte erleben werden: Sie brauchen CRUD für eine Ressource. Nehmen wir an, Sie erstellen eine Todo -App, und Sie benötigen einen RESTful-Controller, um CRUD-Vorgänge für Task Ressource zu verwalten.

Das Mindeste, was Sie benötigen, ist eine Möglichkeit, alle Aufgaben zu lesen und eine neue Aufgabe hinzuzufügen. Dazu benötigen Sie zwei Aktionen: index (häufig Liste auch) und speichern (wird auch create genannt).

Der gemeinsame Fluss in Symfony wäre dies im Pseudocode:

%Vor%

Wenn der Controller ein Service-Locator war

Indexaktion

Wenn die Aktion index ausgeführt wird, wird nur der ManagerRegistry (in diesem Fall Doctrine -Dienst) vom Container aufgelöst. Wir werden es dann bitten, uns ein Aufgaben-Repository zu geben, und wir werden unsere Operation damit durchführen.

Speicheraktion

Wenn speichern Aktion ausgeführt wird, werden wir ein bisschen mehr Arbeit tun: Container bitten, uns FormFactory zu geben, einige Operationen damit zu machen, und dann bitten, es zu geben wir Doktrin und machen auch einige Operationen damit.

Also Zusammenfassung: Wenn eine Indexaktion ausgeführt wird, muss nur ein Dienst vom Dienstcontainer erstellt werden, wenn die Aktualisierung ausgeführt wird, müssen zwei erstellt werden.

Wenn der Controller ein regulärer Dienst war

Sehen wir, was unser Controller benötigt. Im obigen Abschnitt sehen wir, dass FormFactory und Doctrine benötigt werden.

Wenn Sie nun die Aktion index nur aufrufen möchten, um alle Aufgaben aus dem Datenspeicher zu lesen, muss Ihr Controller nach dem Container instanziiert werden. Bevor es instanziiert werden kann, muss Container seine Abhängigkeiten instanziieren: FormFactory und Doctrine. Dann instantiieren Sie den Controller, während Sie diese beiden hinein injizieren.

Sie rufen also index Aktion auf, die FormFactory überhaupt nicht benötigt, aber Sie haben immer noch den Aufwand, es zu erstellen, weil es für eine Aktion benötigt wird nicht in dieser Anfrage überhaupt aufgerufen werden.

Lazy Services

Um diesen Overhead zu reduzieren, gibt es einen faulen Service . Es funktioniert, indem Sie einen Proxy Ihres Dienstes in den Controller einfügen. Soweit es den Controller betrifft, hat es FormFactory bekommen. Was es nicht weiß, ist nicht real FormFactory, sondern ein gefälschtes Objekt, das Aufrufe an echten FormFactory -Code delegiert, wenn Sie eine Methode dafür aufrufen.

Einpacken

Der Controller muss kein Service-Locator sein, kann es aber sein. Es zu einem Service-Locator zu machen, kann etwas leistungsfähiger und einfacher zu laden sein, aber verbirgt Abhängigkeiten. Außerdem ist es etwas schwieriger zu testen, da Sie den Abhängigkeitscontainer vortäuschen müssen. Ob Sie Ihre Controller Services, Funktionen oder Service Locators erstellen möchten, ist Ihre Wahl, und Symfony wird Sie nicht zwingen, eine dieser Methoden zu verwenden.

Nach meiner Erfahrung ist die Erweiterung des Symfony Controllers und die Verwendung von Controllern als Service-Locator einfach , solange Sie nicht Ihre Geschäftslogik in diese schreiben, sondern stattdessen delegiere all diese Arbeiten an Dienste, die du aus dem Container erhältst. Auf diese Weise ist es wirklich unwahrscheinlich, dass Sie Bugs im Controller-Code haben (da Methoden in der Regel aus 2-3 Zeilen Code bestehen) und ohne Test loslegen können.

    
Igor Pantović 10.10.2014 18:50
quelle