Warum sollte man das Publish / Subscribe-Muster (in JS / jQuery) verwenden?

103

Ein Kollege hat mich in das Publish / Subscribe-Muster (in JS / jQuery) eingeführt, aber es fällt mir schwer, herauszufinden, warum man dieses Muster gegenüber "normalem" JavaScript / jQuery verwenden würde.

Zum Beispiel hatte ich vorher den folgenden Code ...

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    var orders = $(this).parents('form:first').find('div.order');
    if (orders.length > 2) {
        orders.last().remove();
    }
});

Und ich konnte das Verdienst sehen, dies stattdessen zu tun, zum Beispiel ...

removeOrder = function(orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    removeOrder($(this).parents('form:first').find('div.order'));
});

Weil es die Möglichkeit einführt, die removeOrderFunktionalität für verschiedene Ereignisse usw. wiederzuverwenden .

Aber warum sollten Sie sich entscheiden, das Publish / Subscribe-Muster zu implementieren und die folgenden Längen zu wählen, wenn es dasselbe tut? (Zu Ihrer Information, ich habe jQuery winzige Kneipe / Sub verwendet )

removeOrder = function(e, orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

Ich habe sicher über das Muster gelesen, kann mir aber nicht vorstellen, warum dies jemals notwendig sein würde. Die Tutorials, in denen erklärt wird, wie dieses Muster implementiert wird, enthalten nur ebenso grundlegende Beispiele wie meine eigenen.

Ich stelle mir vor, dass sich die Nützlichkeit des Pubs / U-Bootes in einer komplexeren Anwendung bemerkbar machen würde, aber ich kann mir keine vorstellen. Ich befürchte, dass ich den Punkt völlig verfehle; aber ich würde gerne wissen, ob es einen gibt!

Könnten Sie erklären , kurz und bündig , warum und in welchen Situationen dieses Muster von Vorteil ist? Lohnt es sich, das Pub / Sub-Muster für Code-Schnipsel wie meine obigen Beispiele zu verwenden?

Maccath
quelle

Antworten:

222

Es geht um lose Kopplung und Einzelverantwortung, was mit MV * -Mustern (MVC / MVP / MVVM) in JavaScript einhergeht, die in den letzten Jahren sehr modern waren.

Lose Kopplung ist ein objektorientiertes Prinzip, bei dem jede Komponente des Systems ihre Verantwortung kennt und sich nicht um die anderen Komponenten kümmert (oder zumindest versucht, sich nicht so sehr wie möglich um sie zu kümmern). Eine lose Kopplung ist eine gute Sache, da Sie die verschiedenen Module problemlos wiederverwenden können. Sie sind nicht mit den Schnittstellen anderer Module gekoppelt. Wenn Sie Publish / Subscribe verwenden, sind Sie nur mit der Publish / Subscribe-Oberfläche verbunden, was keine große Sache ist - nur zwei Methoden. Wenn Sie sich also entscheiden, ein Modul in einem anderen Projekt wiederzuverwenden, können Sie es einfach kopieren und einfügen, und es wird wahrscheinlich funktionieren, oder Sie werden zumindest nicht viel Aufwand benötigen, um es zum Laufen zu bringen.

Wenn wir über lose Kopplung sprechen, sollten wir die Trennung von Bedenken erwähnen. Wenn Sie eine Anwendung mit einem MV * -Architekturmuster erstellen, haben Sie immer ein Modell und eine Ansicht. Das Modell ist der geschäftliche Teil der Anwendung. Sie können es in verschiedenen Anwendungen wiederverwenden. Daher ist es keine gute Idee, es mit der Ansicht einer einzelnen Anwendung zu koppeln, in der Sie es anzeigen möchten, da Sie normalerweise in den verschiedenen Anwendungen unterschiedliche Ansichten haben. Es ist daher eine gute Idee, Publish / Subscribe für die Model-View-Kommunikation zu verwenden. Wenn Ihr Modell ändert, wird ein Ereignis veröffentlicht, die Ansicht fängt es ab und aktualisiert sich selbst. Sie haben keinen Overhead durch das Veröffentlichen / Abonnieren, es hilft Ihnen bei der Entkopplung. Auf die gleiche Weise können Sie beispielsweise Ihre Anwendungslogik im Controller behalten (MVVM, MVP ist nicht gerade ein Controller) und die Ansicht so einfach wie möglich halten. Wenn sich Ihre Ansicht ändert (oder der Benutzer beispielsweise auf etwas klickt), wird nur ein neues Ereignis veröffentlicht, der Controller fängt es ab und entscheidet, was zu tun ist. Wenn Sie mit dem vertraut sindMVC- Muster oder mit MVVM in Microsoft-Technologien (WPF / Silverlight) können Sie sich das Publish / Subscribe wie das Observer-Muster vorstellen . Dieser Ansatz wird in Frameworks wie Backbone.js, Knockout.js (MVVM) verwendet.

Hier ist ein Beispiel:

//Model
function Book(name, isbn) {
    this.name = name;
    this.isbn = isbn;
}

function BookCollection(books) {
    this.books = books;
}

BookCollection.prototype.addBook = function (book) {
    this.books.push(book);
    $.publish('book-added', book);
    return book;
}

BookCollection.prototype.removeBook = function (book) {
   var removed;
   if (typeof book === 'number') {
       removed = this.books.splice(book, 1);
   }
   for (var i = 0; i < this.books.length; i += 1) {
      if (this.books[i] === book) {
          removed = this.books.splice(i, 1);
      }
   }
   $.publish('book-removed', removed);
   return removed;
}

//View
var BookListView = (function () {

   function removeBook(book) {
      $('#' + book.isbn).remove();
   }

   function addBook(book) {
      $('#bookList').append('<div id="' + book.isbn + '">' + book.name + '</div>');
   }

   return {
      init: function () {
         $.subscribe('book-removed', removeBook);
         $.subscribe('book-aded', addBook);
      }
   }
}());

Ein anderes Beispiel. Wenn Ihnen der MV * -Ansatz nicht gefällt, können Sie etwas anderes verwenden (es gibt einen Schnittpunkt zwischen dem, den ich als nächstes beschreibe, und dem zuletzt genannten). Strukturieren Sie Ihre Anwendung einfach in verschiedene Module. Schauen Sie sich zum Beispiel Twitter an.

Twitter-Module

Wenn Sie sich die Benutzeroberfläche ansehen, haben Sie einfach verschiedene Felder. Sie können sich jede Box als ein anderes Modul vorstellen. Zum Beispiel können Sie einen Tweet posten. Diese Aktion erfordert die Aktualisierung einiger Module. Zunächst müssen Ihre Profildaten aktualisiert werden (Feld oben links), aber auch Ihre Zeitachse. Natürlich können Sie Verweise auf beide Module beibehalten und sie separat über ihre öffentliche Oberfläche aktualisieren, aber es ist einfacher (und besser), nur ein Ereignis zu veröffentlichen. Dies erleichtert die Änderung Ihrer Anwendung aufgrund der lockeren Kopplung. Wenn Sie ein neues Modul entwickeln, das von neuen Tweets abhängt, können Sie einfach das Ereignis "Publish-Tweet" abonnieren und damit umgehen. Dieser Ansatz ist sehr nützlich und kann Ihre Anwendung sehr entkoppelt machen. Sie können Ihre Module sehr einfach wiederverwenden.

Hier ist ein grundlegendes Beispiel für den letzten Ansatz (dies ist kein originaler Twitter-Code, sondern nur ein Beispiel von mir):

var Twitter.Timeline = (function () {
   var tweets = [];
   function publishTweet(tweet) {
      tweets.push(tweet);
      //publishing the tweet
   };
   return {
      init: function () {
         $.subscribe('tweet-posted', function (data) {
             publishTweet(data);
         });
      }
   };
}());


var Twitter.TweetPoster = (function () {
   return {
       init: function () {
           $('#postTweet').bind('click', function () {
               var tweet = $('#tweetInput').val();
               $.publish('tweet-posted', tweet);
           });
       }
   };
}());

Für diesen Ansatz gibt es einen ausgezeichneten Vortrag von Nicholas Zakas . Für den MV * -Ansatz werden die besten mir bekannten Artikel und Bücher von Addy Osmani veröffentlicht .

Nachteile: Sie müssen vorsichtig sein, wenn Publish / Subscribe übermäßig verwendet wird. Wenn Sie Hunderte von Ereignissen haben, kann es sehr verwirrend werden, alle zu verwalten. Es kann auch zu Kollisionen kommen, wenn Sie den Namespace nicht verwenden (oder ihn nicht richtig verwenden). Eine erweiterte Implementierung von Mediator, die einem Publish / Subscribe ähnelt, finden Sie hier https://github.com/ajacksified/Mediator.js . Es hat einen Namespace und Funktionen wie "Bubbling", die natürlich unterbrochen werden können. Ein weiterer Nachteil von Publish / Subscribe ist das Testen harter Einheiten. Es kann schwierig werden, die verschiedenen Funktionen in den Modulen zu isolieren und unabhängig voneinander zu testen.

Minko Gechev
quelle
3
Danke, das macht Sinn. Ich bin mit dem MVC-Muster vertraut, da ich es die ganze Zeit mit PHP verwende, aber ich hatte nicht darüber nachgedacht, was die ereignisgesteuerte Programmierung betrifft. :)
Maccath
2
Danke für diese Beschreibung. Hat mir wirklich geholfen, mich mit dem Konzept zu beschäftigen.
Flybear
1
Das ist eine hervorragende Antwort. Konnte mich nicht davon abhalten, dies zu
verbessern
1
Tolle Erklärung, mehrere Beispiele, weitere Lesevorschläge. A ++.
Carson
16

Das Hauptziel besteht darin, die Kopplung zwischen den Codes zu verringern. Es ist eine etwas ereignisbasierte Denkweise, aber die "Ereignisse" sind nicht an ein bestimmtes Objekt gebunden.

Ich werde unten ein großes Beispiel in einem Pseudocode schreiben, der ein bisschen wie JavaScript aussieht.

Nehmen wir an, wir haben ein Klassenradio und ein Klassenrelais:

class Relay {
    function RelaySignal(signal) {
        //do something we don't care about right now
    }
}

class Radio {
    function ReceiveSignal(signal) {
        //how do I send this signal to other relays?
    }
}

Immer wenn das Radio ein Signal empfängt, möchten wir, dass mehrere Relais die Nachricht auf irgendeine Weise weiterleiten. Die Anzahl und Art der Relais kann unterschiedlich sein. Wir könnten es so machen:

class Radio {
    var relayList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function ReceiveSignal(signal) {
        for(relay in relayList) {
            relay.Relay(signal);
        }
    }

}

Das funktioniert gut. Stellen Sie sich nun vor, wir möchten, dass eine andere Komponente auch an den Signalen teilnimmt, die die Radio-Klasse empfängt, nämlich Lautsprecher:

(Entschuldigung, wenn die Analogien nicht erstklassig sind ...)

class Speakers {
    function PlaySignal(signal) {
        //do something with the signal to create sounds
    }
}

Wir könnten das Muster noch einmal wiederholen:

class Radio {
    var relayList = [];
    var speakerList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function AddSpeaker(speaker) {
        speakerList.add(speaker)
    }

    function ReceiveSignal(signal) {

        for(relay in relayList) {
            relay.Relay(signal);
        }

        for(speaker in speakerList) {
            speaker.PlaySignal(signal);
        }

    }

}

Wir könnten dies noch verbessern, indem wir eine Schnittstelle wie "SignalListener" erstellen, sodass wir nur eine Liste in der Radio-Klasse benötigen und immer dieselbe Funktion für jedes Objekt aufrufen können, das das Signal abhören möchte. Dies schafft jedoch immer noch eine Kopplung zwischen der Schnittstelle / Basisklasse / usw., für die wir uns entscheiden, und der Radio-Klasse. Grundsätzlich müssen Sie bei jedem Wechsel einer Radio-, Signal- oder Relay-Klasse darüber nachdenken, wie sich dies möglicherweise auf die beiden anderen Klassen auswirken könnte.

Versuchen wir jetzt etwas anderes. Erstellen wir eine vierte Klasse mit dem Namen RadioMast:

class RadioMast {

    var receivers = [];

    //this is the "subscribe"
    function RegisterReceivers(signaltype, receiverMethod) {
        //if no list for this type of signal exits, create it
        if(receivers[signaltype] == null) {
            receivers[signaltype] = [];
        }
        //add a subscriber to this signal type
        receivers[signaltype].add(receiverMethod);
    }

    //this is the "publish"
    function Broadcast(signaltype, signal) {
        //loop through all receivers for this type of signal
        //and call them with the signal
        for(receiverMethod in receivers[signaltype]) {
            receiverMethod(signal);
        }
    }
}

Jetzt haben wir ein Muster , das uns bekannt ist, und wir können es für eine beliebige Anzahl und Art von Klassen verwenden, solange sie:

  • kennen den RadioMast (die Klasse, die die gesamte Nachrichtenübergabe verarbeitet)
  • kennen die Methodensignatur zum Senden / Empfangen von Nachrichten

Also ändern wir die Radio-Klasse in ihre endgültige, einfache Form:

class Radio {
    function ReceiveSignal(signal) {
        RadioMast.Broadcast("specialradiosignal", signal);
    }
}

Und wir fügen die Lautsprecher und das Relais zur Empfängerliste des RadioMast für diese Art von Signal hinzu:

RadioMast.RegisterReceivers("specialradiosignal", speakers.PlaySignal);
RadioMast.RegisterReceivers("specialradiosignal", relay.RelaySignal);

Jetzt hat die Speakers and Relay-Klasse keine Kenntnis von irgendetwas, außer dass sie über eine Methode verfügt, die ein Signal empfangen kann, und die Radio-Klasse als Herausgeber ist sich des RadioMast bewusst, an den sie Signale veröffentlicht. Dies ist der Punkt, an dem ein Nachrichtenübermittlungssystem wie Publish / Subscribe verwendet wird.

Anders Arpi
quelle
Es ist wirklich toll, ein konkretes Beispiel zu haben, das zeigt, wie die Implementierung des Pub / Sub-Musters besser sein kann als die Verwendung "normaler" Methoden! Danke dir!
Maccath
1
Bitte! Persönlich stelle ich oft fest, dass mein Gehirn bei neuen Mustern / Methoden nicht „klickt“, bis ich ein tatsächliches Problem erkenne, das es für mich löst. Das Sub- / Pub-Muster eignet sich hervorragend für Architekturen, die konzeptionell eng miteinander verbunden sind, aber wir möchten sie dennoch so weit wie möglich voneinander trennen. Stellen Sie sich ein Spiel vor, in dem Sie Hunderte von Objekten haben, die alle auf Dinge reagieren müssen, die um sie herum geschehen, und diese Objekte können alles sein: Spieler, Kugel, Baum, Geometrie, GUI usw. usw.
Anders Arpi
3
JavaScript hat das classSchlüsselwort nicht. Bitte betonen Sie diese Tatsache, z. indem Sie Ihren Code als Pseudocode klassifizieren.
Rob W
Tatsächlich gibt es in ES6 ein Klassenschlüsselwort.
Minko Gechev
5

Die anderen Antworten haben großartige Arbeit geleistet, um zu zeigen, wie das Muster funktioniert. Ich wollte die implizite Frage " Was ist los mit dem alten Weg?" " Ansprechen, da ich kürzlich mit diesem Muster gearbeitet habe, und ich finde, dass es eine Veränderung in meinem Denken beinhaltet.

Stellen Sie sich vor, wir haben ein Wirtschaftsbulletin abonniert. Das Bulletin veröffentlicht eine Überschrift: " Senken Sie den Dow Jones um 200 Punkte ". Das wäre eine seltsame und etwas verantwortungslose Nachricht. Wenn jedoch veröffentlicht wurde: " Enron hat heute Morgen einen Insolvenzschutz nach Kapitel 11 beantragt ", ist dies eine nützlichere Nachricht. Beachten Sie, dass die Nachricht verursachen kann der Dow Jones um 200 Punkte fällt, aber das ist eine andere Sache.

Es gibt einen Unterschied zwischen dem Senden eines Befehls und dem Hinweis auf etwas, das gerade passiert ist. Nehmen Sie vor diesem Hintergrund Ihre Originalversion des Pub / Sub-Musters und ignorieren Sie den Handler vorerst:

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

Hier besteht bereits eine implizite starke Kopplung zwischen der Benutzeraktion (ein Klick) und der Systemantwort (eine Bestellung wird entfernt). In Ihrem Beispiel gibt die Aktion effektiv einen Befehl aus. Betrachten Sie diese Version:

$.subscribe('iquery/action/remove-order-requested', handleRemoveOrderRequest);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order-requested', $(this).parents('form:first').find('div.order'));
});

Jetzt reagiert der Handler auf etwas Interessantes, das passiert ist, ist jedoch nicht verpflichtet, eine Bestellung zu entfernen. Tatsächlich kann der Handler alle möglichen Dinge tun, die nicht direkt mit dem Entfernen eines Auftrags zusammenhängen, aber dennoch möglicherweise für die aufrufende Aktion relevant sind. Beispielsweise:

handleRemoveOrderRequest = function(e, orders) {
    logAction(e, "remove order requested");
    if( !isUserLoggedIn()) {
        adviseUser("You need to be logged in to remove orders");
    } else if (isOkToRemoveOrders(orders)) {
        orders.last().remove();
        adviseUser("Your last order has been removed");
        logAction(e, "order removed OK");
    } else {
        adviseUser("Your order was not removed");
        logAction(e, "order not removed");
    }
    remindUserToFloss();
    increaseProgrammerBrowniePoints();
    //etc...
}

Die Unterscheidung zwischen einem Befehl und einer Benachrichtigung ist eine nützliche Unterscheidung mit diesem Muster, IMO.

Trevedhek
quelle
Wenn sich Ihre letzten 2 Funktionen ( remindUserToFloss& increaseProgrammerBrowniePoints) in separaten Modulen befinden würden, würden Sie zwei Ereignisse direkt nacheinander direkt dort veröffentlichen handleRemoveOrderRequestoder hätten Sie flossModuleein Ereignis in einem browniePointsModul veröffentlicht, wenn remindUserToFloss()dies abgeschlossen ist?
Bryan P
4

Damit Sie keine Methoden- / Funktionsaufrufe fest codieren müssen, veröffentlichen Sie das Ereignis einfach, ohne sich darum zu kümmern, wer zuhört. Dies macht den Publisher unabhängig vom Abonnenten und reduziert die Abhängigkeit (oder Kopplung, egal welchen Begriff Sie bevorzugen) zwischen zwei verschiedenen Teilen der Anwendung.

Hier sind einige Nachteile der Kopplung, wie in Wikipedia erwähnt

Eng gekoppelte Systeme weisen tendenziell die folgenden Entwicklungseigenschaften auf, die häufig als Nachteile angesehen werden:

  1. Eine Änderung in einem Modul erzwingt normalerweise einen Welligkeitseffekt von Änderungen in anderen Modulen.
  2. Die Montage von Modulen erfordert aufgrund der erhöhten Abhängigkeit zwischen Modulen möglicherweise mehr Aufwand und / oder Zeit.
  3. Ein bestimmtes Modul ist möglicherweise schwieriger wiederzuverwenden und / oder zu testen, da abhängige Module enthalten sein müssen.

Stellen Sie sich so etwas wie ein Objekt vor, das Geschäftsdaten kapselt. Es hat einen fest codierten Methodenaufruf, um die Seite zu aktualisieren, wenn das Alter festgelegt wird:

var person = {
    name: "John",
    age: 23,

    setAge: function( age ) {
        this.age = age;
        showAge( age );
    }
};

//Different module

function showAge( age ) {
    $("#age").text( age );
}

Jetzt kann ich das Personenobjekt nicht testen, ohne auch das einzuschließen showAge Funktion einzuschließen. Wenn ich das Alter auch in einem anderen GUI-Modul anzeigen muss, muss ich diesen Methodenaufruf fest codieren .setAge, und jetzt gibt es Abhängigkeiten für zwei nicht verwandte Module im person-Objekt. Es ist auch nur schwer zu pflegen, wenn Sie sehen, dass diese Anrufe getätigt werden und sie sich nicht einmal in derselben Datei befinden.

Beachten Sie, dass Sie innerhalb desselben Moduls natürlich direkte Methodenaufrufe haben können. Geschäftsdaten und oberflächliches GUI-Verhalten sollten sich jedoch nach vernünftigen Maßstäben nicht im selben Modul befinden.

Esailija
quelle
Ich verstehe das Konzept der "Abhängigkeit" hier nicht; Wo ist die Abhängigkeit in meinem zweiten Beispiel und wo fehlt sie in meinem dritten? Ich kann keinen praktischen Unterschied zwischen meinem zweiten und dritten Snippet feststellen - es scheint nur eine neue Ebene zwischen der Funktion und dem Ereignis ohne wirklichen Grund hinzuzufügen. Ich bin wahrscheinlich blind, aber ich denke, ich brauche mehr Hinweise. :(
Maccath
1
Könnten Sie einen Anwendungsbeispiel angeben, bei dem Publish / Subscribe besser geeignet wäre, als nur eine Funktion zu erstellen, die dasselbe ausführt?
Jeffrey Sweeney
@Maccath Einfach ausgedrückt: Im dritten Beispiel wissen Sie nicht oder müssen wissen, dass es removeOrderüberhaupt existiert, sodass Sie nicht davon abhängig sein können. Im zweiten Beispiel müssen Sie wissen.
Esailija
Obwohl ich immer noch der Meinung bin, dass es bessere Möglichkeiten gibt, das zu tun, was Sie hier beschrieben haben, bin ich zumindest davon überzeugt, dass diese Methode einen Zweck hat, insbesondere in Umgebungen mit vielen anderen Entwicklern. +1
Jeffrey Sweeney
1
@Esailija - Danke, ich glaube ich verstehe ein bisschen besser. Also ... wenn ich den Abonnenten komplett entfernen würde, würde es keinen Fehler machen oder so, es würde einfach nichts tun? Und würden Sie sagen, dass dies in einem Fall nützlich sein könnte, in dem Sie eine Aktion ausführen möchten, aber nicht unbedingt wissen, welche Funktion zum Zeitpunkt der Veröffentlichung am relevantesten ist, der Abonnent sich jedoch in Abhängigkeit von anderen Faktoren ändern könnte?
Maccath
1

Die Implementierung von PubSub wird häufig dort gesehen, wo -

  1. Es gibt eine Portlet-ähnliche Implementierung, bei der mehrere Portlets mithilfe eines Ereignisbusses kommunizieren. Dies hilft beim Erstellen in einer aync-Architektur.
  2. In einem System, das durch eine enge Kopplung beeinträchtigt ist, ist pubsub ein Mechanismus, der bei der Kommunikation zwischen verschiedenen Modulen hilft.

Beispielcode -

var pubSub = {};
(function(q) {

  var messages = [];

  q.subscribe = function(message, fn) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(fn);
  }

  q.publish = function(message) {
    /* fetch all the subscribers and execute*/
    if (!messages[message]) {
      return false;
    } else {
      for (var message in messages) {
        for (var idx = 0; idx < messages[message].length; idx++) {
          if (messages[message][idx])
            messages[message][idx]();
        }
      }
    }
  }
})(pubSub);

pubSub.subscribe("event-A", function() {
  console.log('this is A');
});

pubSub.subscribe("event-A", function() {
  console.log('booyeah A');
});

pubSub.publish("event-A"); //executes the methods.
user2756335
quelle
1

Das Papier "Die vielen Gesichter des Publizierens / Abonnierens" ist eine gute Lektüre und eine Sache, die sie hervorheben, ist die Entkopplung in drei "Dimensionen". Hier ist meine grobe Zusammenfassung, aber bitte beziehen Sie sich auch auf das Papier.

  1. Raumentkopplung.Die interagierenden Parteien müssen sich nicht kennen. Der Verlag weiß nicht, wer zuhört, wie viele zuhören oder was sie mit der Veranstaltung machen. Die Abonnenten wissen nicht, wer diese Veranstaltungen produziert, wie viele Produzenten es gibt usw.
  2. Zeitentkopplung. Die interagierenden Parteien müssen während der Interaktion nicht gleichzeitig aktiv sein. Beispielsweise kann ein Abonnent getrennt werden, während ein Herausgeber einige Ereignisse veröffentlicht, er kann jedoch darauf reagieren, wenn er online geschaltet wird.
  3. Synchronisationsentkopplung. Publisher werden beim Produzieren von Ereignissen nicht blockiert, und Abonnenten können asynchron durch Rückrufe benachrichtigt werden, wenn ein von ihnen abonniertes Ereignis eintrifft.
Peheje
quelle
0

Einfache Antwort Die ursprüngliche Frage suchte nach einer einfachen Antwort. Hier ist mein Versuch.

Javascript bietet keinen Mechanismus für Codeobjekte, um ihre eigenen Ereignisse zu erstellen. Sie benötigen also eine Art Ereignismechanismus. Das Publish / Subscribe-Muster erfüllt diese Anforderungen, und Sie müssen einen Mechanismus auswählen, der Ihren eigenen Anforderungen am besten entspricht.

Jetzt sehen wir einen Bedarf für das Pub / Sub-Muster. Müssen Sie dann lieber DOM-Ereignisse anders behandeln als Ihre Pub / Sub-Ereignisse? Um die Komplexität und andere Konzepte wie die Trennung von Bedenken (SoC) zu reduzieren, sehen Sie möglicherweise den Vorteil, dass alles einheitlich ist.

Paradoxerweise führt mehr Code zu einer besseren Trennung von Bedenken, was sich gut auf sehr komplexe Webseiten skalieren lässt.

Ich hoffe, jemand findet dies eine ausreichend gute Diskussion, ohne ins Detail zu gehen.

Simon Miller
quelle