Ich möchte eine lockere Kopplung in meinem PHP-Code fördern, indem ich bestimmte Klassen beobachtbar mache. Symfonys EventDispatcher-Komponente sieht vielversprechend aus, ebenso wie der SPL SplObserver / SplSubject paar Klassen.
Was ist der beste Weg, dies zu tun? Ich kann ein paar verschiedene Möglichkeiten sehen:
(1) Injizieren Sie eine EventDispatcher-Instanz in jede beobachtbare Klasse (verfolgen Sie eine globale EventDispatcher-Instanz):
%Vor%(2) Lassen Sie die Observable-Klasse die EventDispatcher-Klasse erweitern:
%Vor%(3) Verwenden Sie SplObserver / SplSubject - einfach, aber nicht so flexibel wie die EventDispatcher-Komponente
Disclaimer: Diese Antwort hat nichts mit dem Symfony EventDispatcher zu tun, sondern alles, was mit Ihrer Frage zu tun hat. Wenn Sie nur eine Antwort wollen, können Sie die (etwas) akademische Diskussion überspringen und zum Ende springen.
FACT : Die zunehmende Anwendungsgröße bedeutet eine kongruente Zunahme der Komplexität.
Wenn der Umfang Ihrer Anwendung wächst, werden Sie immer mehr Klassen hinzufügen, um die erforderliche Funktionalität zu implementieren. Plötzlich ist es nicht mehr so einfach sich daran zu erinnern, dass ein Foo
-Objekt eine bestimmte Aktion ausführen muss, wenn ein Bar
-Objekt erstellt wird. Darüber hinaus wird es zunehmend schwieriger, die notwendigen Beziehungen aufrechtzuerhalten, ohne dass die Objekte sehr eng miteinander verbunden sind. Wenn Ihre Objekte damit beginnen, einander komplementäre Funktionen zur Verfügung zu stellen, werden sie zunehmend schwieriger.
Wir brauchen einen Weg, wie Objekte kommunizieren können, ohne explizite Referenzen hart zu codieren, die wir vergessen zu ändern, wenn sich etwas ändert. Wie verwalten wir diese Art von miteinander in Beziehung stehender Funktionalität zwischen Knoten eines schnell wachsenden Objektgraphen?
Nehmen wir einen kleinen Umweg für einen Moment und betrachten Sie eine romantische Metapher ...
Jede Beziehung erfordert konsistente Kommunikation, wenn sie dauern wird. Sicher, du und dein Partner können sich am Samstagabend für zwielichtige Treffen treffen und den Rest der Woche nicht miteinander reden. Diese Art von Kommunikation führt jedoch normalerweise zu einer brüchigen Beziehung, in der keine der beiden Parteien versteht, was der andere im Kontext der Beziehung eigentlich gut zu tun braucht.
Indem Sie die Analogie fortführen, während sich Ihre Persönlichkeit im Laufe der Zeit langsam verändert (und dies auch tut), verhindert dieser Mangel an Kommunikation, dass Ihr Partner versteht, wie Sie am besten mit Ihnen interagieren können. Irgendwann kommen alle gebrochenen Versprechen und verpassten Anrufe zu einem Höhepunkt und die Beziehung funktioniert einfach nicht mehr. Es ist kaputt.
Ihre Anwendung funktioniert auf die gleiche Weise. Code sollte reif genug sein, um zu sagen: "Hey Baby, ich könnte mich ändern, aber wenn ich es tue, verspreche ich dir, ich werde dich immer wissen lassen, was mit mir los ist." Da die Komplexität zunimmt, gestaltet sich die Kommunikation aufgrund des traditionellen geradlinigen Anwendungsdesigns leider schwierig, ohne dass eine enge Kopplung zwischen den Klassen erforderlich ist.
Und darum geht es beim Event-Management. Das Ziel besteht darin, unseren Objekten die Möglichkeit zu geben, so miteinander zu kommunizieren, dass Beziehungen zu den Objekten, mit denen sie kommunizieren müssen, nicht fest codiert werden. Wie bei den meisten Programmierproblemen gibt es keinen singulären, spezifischen, "richtigen" Weg, dies zu tun. Ihre Frage bezieht sich speziell auf zwei der verfügbaren Methoden, um dies zu erreichen, also werde ich diese ansprechen. Wenn du etwas über andere Optionen erfahren möchtest, hat @ircmaxell kürzlich einen schönen Umfrage-Blog gepostet: postet, dass PHP-Apps "Pluggable" werden sollen .
In der Praxis finden Sie nur wenige echte PHP-Anwendungen für das Observer-Muster. Der Grund dafür ist, dass der Code sehr dynamisch sein kann. Es dauert nicht lange, bis Sie den Objekten überall Beobachter hinzufügen.
Wenn das passiert, hast du die lose Kopplung, die du versucht hast zu erreichen, aber du hast ein anderes Problem geschaffen: das manuelle Anhängen aller Beobachter und Subjekte . Zum Beispiel haben Sie eine Menge Arbeit für sich selbst geschaffen, wenn jede Klasse in Ihrer Anwendung Gegenstand eines Beobachterobjekts Logger
ist. Auch IMHO verdeckt diese Methode manchmal Ihre API, indem Sie Dinge verschieben, die genauer als tatsächliche Abhängigkeiten des Subjekts aus der Methodensignatur des Subjektkonstruktors beschrieben werden können.
Unsere Anwendung wäre viel flexibler, wenn wir einen zentralen Dispatcher verwenden würden, um interessierte Objekte zu benachrichtigen, wenn ein Ereignis eintritt, obwohl das Observer-Muster ideal für einmalige oder einfache Fälle sein kann.
Ein robusterer Weg zur Verwaltung von Ereignissen besteht darin, eine zentralisierte Schicht einzufügen, um Dispatchereignisse an die entsprechenden Listener zu senden. Das ist das Mediator Wiki -Muster (und das Symfony-Event) Dispatcher).
Der Zweck eines Mediators ist, dass es für jedes Ereignis im System eine zentrale Transitstation ist, so dass es über den gesamten Umfang der Anwendung (oder der vermittelten Teile) zugänglich sein muss. Beachte: nicht bedeutet, dass du es als globale Variable behandeln und mit globalen Schlüsselwörtern auf Mediator zugreifen oder es in eine Art böses Singleton-Objekt oder eine statische Eigenschaft / Methode einbetten sollst. Diese Art von Missbrauch führt zu den in der ersten Antwort genannten Problemen @liquorvicar.Ich stimme jedoch der Bewertung dieser Antwort nicht zu, dass:
"Wenn Sie einen eventDispatcher haben, der überall in Ihrer App ist und fast alles tut, kann Ihr Code schwieriger zu testen, zu verstehen / zu warten usw. (er kann sich einem God-Objekt nähern)"
Dies ist nur der Fall, wenn Sie den Mediator missbrauchen; Es sollte Ereignisbenachrichtigungen und nichts anderes versenden. Ich würde Sie davor warnen, es so auszudehnen, wie Sie es in der Option (2) Ihrer Frage vorschlagen. Bei korrekter Anwendung ist ein Mediatorobjekt überaus überprüfbar. Es gibt nichts einfacheres als das Verhalten von Abhängigkeitsobjekten, die in Ihren Konstruktoren angegeben sind. Darum geht es bei Unit-Testing.
Wenn Sie also eine nichtlineare Ereignisverwaltung in Ihrer Anwendung benötigen, würde ich Ihnen die Option (1) aus Ihrer Frage empfehlen. Es ist vollkommen akzeptabel, solange Sie es nicht missbrauchen. Durch das Überschreiben der Symfony-Implementierung scheint es möglich, jedes PHP zu unterstützen, das als Listener aufgerufen werden kann. Persönlich bevorzuge ich ein System, das die lazur-Instanziierung von klassenbasierten Listenern für ein effizienteres und objektorientiertes Paradigma erlaubt, aber die Implementierungsdetails sind Ihnen überlassen.
Das Muster der Verantwortungskette hängt eng mit dem Mediator zusammen und ist eine weitere gültige Methode, um ähnliche Ergebnisse zu erzielen. Wenn Sie interessiert sind, würde ich den Link empfehlen, der früher im @ ircmaxell-Blogpost veröffentlicht wurde.
Ich würde vermeiden (2). Vererbung ist möglicherweise das am häufigsten verwendete Muster und ist wahrscheinlich hier nicht relevant. Die Wahl zwischen den Optionen (1) und (3) hängt wahrscheinlich von Ihrem Kontext ab. Während die Vermeidung einer engen Kopplung gut ist, sollten Sie auf Messerlösungen von swiss-army achten. Wenn Sie einen eventDispatcher haben, der überall in Ihrer App vorhanden ist und fast alles tut, kann Ihr Code schwieriger zu testen, zu verstehen / zu warten usw. werden (er kann sich einem God-Objekt nähern). Auf der anderen Seite ist die Spl-Lösung viel einfacher und wenn Sie also mehrere Beobachter / Observables benötigen, müssen Sie möglicherweise zu viele SplObserver / SplSubjects pflegen.
Wie bei den meisten Dingen in OOP gibt es keinen besten Weg und hängt normalerweise von Ihren genauen Anwendungsfällen ab ...
Tags und Links php design-patterns oop symfony1