Was sollten Plugins verwenden: Hooks, Events oder etwas anderes?

24

Stellen Sie sich eine App vor, mit der Plugins auf ihren Programmfluss reagieren können.

Ich kenne zwei Möglichkeiten, um dies zu erreichen: Hooks und Events

1. Haken

Verwenden Sie Aufrufe, um Funktionen im Hauptprogrammablauf zu leeren. Diese Funktionen können durch Plugins überschrieben werden.

Beispielsweise implementiert Drupal CMS Hooks, die Modulen und Themen zur Verfügung stehen. Hier ist ein Beispiel, wie Hooks in einer file_copy- Funktion implementiert werden.

function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
    // ... [File copying routine]

    // Inform modules that the file has been copied.
    module_invoke_all('file_copy', $file, $source);

    return $file;
    // ...
}

Ein Modul kann eine modulename_file_copy($file, $source)Funktion implementieren , die vom module_invoke_allin aufgerufen wird file_copy. Nach Beendigung dieser Funktion file_copywird die Ausführung fortgesetzt.

2. Ereignisse

Lassen Sie die App Ereignisse versenden, die von den Plugins abgehört werden können. Nachdem ein abonniertes Ereignis empfangen wurde, fängt ein Plugin den Programmablauf ab und führt die erforderlichen Vorgänge aus.

Ein Fotorama-Plugin für die jQuery-Galerie implementiert beispielsweise mehrere Ereignisse . Hier ist ein Teil der showMethode, die das fotorama:showEreignis auslöst.

  that.show = function (options) {
    // ... [show the new frame]

    // [fire the event]
    options.reset || triggerEvent('show', {
      user: options.user,
      time: time
    });

    // ... [do lots of other stuff with navigation bars, etc.]
  };

Ein Skript kann dieses Ereignis abhören und etwas tun, wenn es ausgelöst wird:

$('.fotorama').on(
  'fotorama:show',
  function (e, fotorama, extra) {
    console.log(e.type + (extra.user ? ' after user’s touch' : ''));
    console.log('transition duration: ' + extra.time);
  }
);

FRAGE

  1. Gibt es andere gängige Methoden, um ein solches Plugin-Verhalten zu implementieren?

  2. Wenn nicht, wann sollte man Hooks verwenden und wann sollte man Events verwenden? Ist es das ultimative Ziel, den Code sowohl aus Sicht der App als auch aus Sicht des Plugin-Entwicklers besser zu verwalten und lesbar zu machen ?

sbichenko
quelle

Antworten:

17

Der Hauptunterschied zwischen einem Haken und einem Ereignis ist die lose Kopplung gegenüber der engen Kopplung.

Ein Hook ist eine allgemeine Methode, um zu übertragen, dass etwas passiert ist. Sie können neue Hooks hinzufügen, ohne Plugins neu kompilieren zu müssen. Alle Hooks folgen einem generischen Entwurfsmuster. Sobald die Hook-API definiert ist, ändert sie sich nicht mehr, sodass die Kopplung zwischen der App und dem Plugin wahrscheinlich nicht mehr funktioniert.

Ereignisse sind enger an die App gekoppelt. Ereignisse können Parameter definieren, die an das Ereignis angehängt sind. Wenn Sie diese Parameter ändern, wird die API mit vorhandenen Plug-ins beschädigt.

Sie beide erzielen die gleichen Ergebnisse. Es hängt nur davon ab, wie Sie das Plugin mit der App koppeln möchten.

Hooks können Ihnen eine dynamischere Kopplung bieten, die mit der Veröffentlichung neuer Versionen Ihrer App wahrscheinlich nicht mehr funktioniert. Der Nachteil ist jedoch, dass Sie beim Kompilieren keine Warnungen erhalten, dass die Plugins nicht mehr kompatibel sind.

Ereignisse bieten Ihnen die Möglichkeit, Kompilierungsfehler zu erhalten, die das Plugin ändern muss, da sich einige der Ereignissignaturen geändert haben.

Sie haben nach alternativen Ansätzen gefragt.

Befehle:

Anstelle von Plugins, die auf ausgelöste Ereignisse reagieren. Plugins übertragen Befehlsobjekte an die Anwendung. Jedes Befehlsobjekt implementiert eine Schnittstelle, die von Befehlen verwendet wird. Wenn die Anwendung eine Funktion ausführen muss, werden alle Befehle für diese Funktion ausgeführt. Dies ist sehr ähnlich zu Ereignissen, außer dass es als Objekte anstelle von Rückruffunktionen implementiert ist.

Makros:

Anstelle von Plugins, die reagieren, wenn etwas passiert. Plugins sorgen proaktiv dafür, dass etwas passiert. Ein Makro ist eine kleine Hochsprache, die über der Anwendung ausgeführt wird und angibt, was zu tun ist.

Listener für Statusänderungen:

Ereignisse werden von der Anwendung mit Bedacht des Entwicklers ausgelöst. Der Entwickler muss wissentlich Code schreiben, der das Ereignis ausgibt. Stattdessen besteht ein alternativer Ansatz darin, Objekte automatisch zu senden, wenn sich ihr interner Zustand geändert hat. Entweder die Änderung einer Eigenschaft oder andere Indikatoren. Plugins können dann auf diese spezifischen Statusänderungen warten und entsprechend reagieren. Der Vorteil dieses Ansatzes ist, dass sich der Programmierer nicht an die Übertragung von Ereignissen erinnern muss. Es könnte sich beispielsweise um ein Document-Objekt handeln, und der Programmierer setzt ein Flag, um zu kennzeichnen, dass das Dokument gespeichert werden muss. Diese Statusänderung wird an Listening-Plugins gesendet. Möglicherweise gibt es ein Plugin, das den Dokumenttitel so ändert, dass ein Sternchen angezeigt wird.

Reactgular
quelle
2
+1 für die Alternativen, -1 für die Definitionen und das Kopplungs Argument (das tut exist aber Kopplung eine Folge von Design - Entscheidungen ist, je nachdem , was Name , den Sie Ihr Plugin - System geben)
5
Ich denke, Sie machen auch Annahmen darüber, wie ein Ereignis vom Generator zum Beobachter / Zuhörer wandert. In der Tat ist es umgekehrt, Haken sind eng miteinander verbunden, Ereignisse hingegen nicht.
Ahmed Masud
3

Auf jeden Fall Ereignisse, es ermöglicht die notwendige Abstraktion bereits auf der architektonischen Ebene.

Erwarten Sie nicht, dass jemand, der ein Plugin schreibt, dies tatsächlich als dokumentiert oder in irgendeiner Weise korrekt tut. Ich habe eine gut dokumentierte API mit Millionen von Benutzern gepflegt und kann Ihnen aus einer sehr schmerzhaften Erfahrung sagen, dass im Grunde niemand die Dokumentation liest und fast niemand die API richtig verwendet.

Nehmen Sie das folgende Beispiel mit Hooks: Sie haben ein System, auf dem 20 Plugins ausgeführt werden. Eines dieser Plugins ruft die file_copyMethode so auf, wie sie dokumentiert ist, und erwartet ein dokumentiertes Ergebnis. Aber ein anderes Plugin hat diese Funktion aktiviert und eines der folgenden Probleme führt zu einem Absturz oder einer Fehlfunktion:

  • Die Hook-Funktion stürzt einfach ab. Alle anderen Plugins sind jetzt aufgeschraubt, weil sie file_copy nicht mehr können oder die Funktion anders funktioniert als erwartet.
  • Die Eingabe ist basierend auf der Dokumentation korrekt, aber das andere Plugin erwartet es nicht und liefert seltsame Ergebnisse oder Abstürze.
  • Der Aufruf funktioniert einwandfrei, aber das Ergebnis entspricht nicht mehr den Erwartungen der Dokumentation, sodass das Plugin ausfällt oder abstürzt.

Wenn Sie dasselbe wie oben bei Ereignissen mit denselben Problemen in diesen Plugins tun, geschieht Folgendes:

  • Die Event-Funktion von Plugin X stürzt ab, aber alle anderen funktionieren einwandfrei. Da diese Plugins jedoch nicht miteinander verbunden sind, können Sie das abstürzende Plugin einfach deaktivieren, während die anderen weiterhin einwandfrei funktionieren.
  • Seltsame Eingaben können von Ihrer Funktion korrekt verarbeitet werden und Sie können für jedes Plugin einzeln nach allen möglichen Dingen suchen. Der Plugin-Entwickler hat jetzt eine stabile und zuverlässige Methode, um sein Plugin tatsächlich zu testen. Dadurch kann er sicher sein, dass es für alle funktioniert, wenn es für ihn funktioniert. Wenn ein Plugin eine falsche Eingabe liefert, kann es für dieses eine Plugin isoliert werden.
  • Ebenso kann das Ergebnis unter allen Umständen korrekt überprüft und definiert werden, sodass der Plugin-Entwickler eine stabile und zuverlässige Antwort auf diese Funktion hat, die er testen kann.
TwoThe
quelle
1

Vererbung kann eine Option sein.

Anders als bei Hooks erfordert die Vererbung keine zusätzlichen Methodendefinitionen und es gibt keinen Leistungsverlust beim Aufrufen der leeren Methode, falls nichts angeschlossen ist.

Im Gegensatz zu Ereignissen benötigt die Vererbung keinen zusätzlichen Code für den Ereignisaufruf.

Die Vererbung funktioniert jedoch am besten, wenn nur ein Plugin einen Verhaltenstyp ändert. Wenn Sie viele Plugins benötigen, müsste das zweite von dem ersten usw. abgeleitet werden, was nicht angemessen ist.

Thomas Weller
quelle
-1 Da Sie Vererbung verwenden und dann den Instantiierungscode ändern, um Ihre Spezifikation zu verwenden und Vererbung zu missbrauchen, da das neue Verhalten einen anderen Zweck als die Hauptanwendung hat ...
SparK
0

Auf jeden Fall Ereignisse. Dadurch kann Ihre Architektur breiter skaliert werden.

Stellen Sie sich vor, was passiert, wenn Sie Ihr Plugin beispielsweise auf einem separaten Computer installieren müssen. Verwenden von Ereignissen - Sie müssen nur einen kleinen Teil des Codes ändern, um Ihr Ereignisnetzwerk zu optimieren.

vpol
quelle