Laden...

Event weitergeben über mehrere Applikationsschichten

Erstellt von HannesB vor 13 Jahren Letzter Beitrag vor 13 Jahren 6.036 Views
HannesB Themenstarter:in
185 Beiträge seit 2005
vor 13 Jahren
Event weitergeben über mehrere Applikationsschichten

Hallo,

wir haben soeben innerhalb unsere Entwicklerteams darüber diskutiert,
sind derzeit noch zu keinem finalen Ergebnis gekommen, weshalb ich euch gerne um eure Meinung fragen möchte.

Szenario:
Es gibt einen Importprozess, welcher ein Protokoll schreibt.
Der Importprozess verwendet eine Instanz der Klasse "ClientImporter".
Der "ClientImporter" wiederum verwendet intern (private) weitere Komponenten, nennen wir sie Komponente A und B.
Komponente "A" löst nun eine Event aus (z.B. Datei nicht gefunden), welches ins Protokoll geschrieben werden soll.

Importprozess (schreibt Protokoll) => Importer => Komponente A, B (lösen Event aus),... ev. weitere Komponenten/Sub-Komponenten

Wie kann man nun elegant dieses Event "nach oben" geben, an den Importprozess, sodass dieser es ins Protokoll schreiben kann?

  1. Möglichkeit: Bubbling von Events
    Komponente A bietet Eventhandler für "File not found" - der übergeordnete Importer registiert sich an diesem Event.
    Der Importer seinerseits bietet ebenfalls ein Event "File not found" - der Import-Prozess registriert sich an diesem Event.
    Nachteil: Doppelte Events, Events müssen "weitergegeben" werden

  2. Möglichkeit: Domain Event Pattern oder Mediator Pattern verwenden.
    Es gibt eine zentrale Stelle, an welche Events (Messages) geschickt werden.
    Andere Komponenten z.B. der Importprozess können sich an dieser Stelle registrieren, um die Meldungen zu bekommen.
    Komponenten schicken Meldungen an diese "zentrale Stelle".
    Nachteil: "Mediator" muss static sein und bekommt u.U. Events von mehreren Importprozessen gleichzeitig.

Zum Hintergrund: Die Applikation wird als WCF Service gehostet und es werden mehrere Instanzen erzeugt.
Diese Instanzen würden sich einen statischen Mediator "teilen" - wenn 10 Importprozesse laufen, würde dieser alle Meldungen bekommen.
Abgesehen davon, dass das wohl nicht so gut ist, kann eine Meldungen dann nicht mehr ohne weiters einem spezifischen Importprozess zugeordnet werden.

  1. Möglichkeit: Wie 2., jedoch erzeugt der Importprozess eine Mediatorinstanz, dieses ist daher nicht static.
    Der Mediator würde dann Meldungen von einem Importprozess bekommen, ABER: Diese Mediatorinstanz müsste man weitergeben an die untergeordneten Komponenten,
    welche ja ebenfalls an diese Messages "posten" müssen.
    Der "Importer" und die Komponenten A und B müssen also den Mediator kennen...was wohl nicht Sinn der Sache ist, weil diese imho. nichts damit zu tun haben sollten (single responsible principle).

Ev. wird später mal WF verwendet und k.A., wie es sich da dann verhält, wenn man statische Instanzen (Möglichkeit 2) verwendet -
wenn möglich möchte ich statische Instanzen aus bekannten Gründen (z.B. Skalierung) eher generell vermeiden.

Derzeit tentiere ich zu Möglichkeit 1.
Gefallen tut mir die Lösung allerding nicht, weil ich hier Events mehrfach ausführen muss und die übergeordnete Klasse die Events ihrer Komponente(n) weitergeben muss.

Wie würdet ihr hier vorgehen?

thx
Hannes

U
400 Beiträge seit 2008
vor 13 Jahren

Eine Publiceigenschaft mit dem Typ des Events in jeder Schicht einbauen , sodass das Eventobject bis ganz nach oben durchgereicht werden kann. (Ungetestet)

Oder einfach in jeder Schicht das Event implementieren

HannesB Themenstarter:in
185 Beiträge seit 2005
vor 13 Jahren

hallo und danke für deine Antwort,

Eine Publiceigenschaft mit dem Typ des Events in jeder Schicht einbauen , sodass das Eventobject bis ganz nach oben durchgereicht werden kann. (Ungetestet)

Das müsste dann gar nicht public sein, intern würde reichen, nur die oberste Schicht muss es als public bereitstellen - das wäre "Lösung 1", welche imho. ja auch nicht ganz optimal ist.

Oder einfach in jeder Schicht das Event implementieren

Der Importprozess soll die Komponente A, B, ... nicht kennen d.h. von diesen ein Event direkt abonieren, Komponenten A, B, x, ... werden intern vom "Import" verwendet - hier soll keine interne Logik nach außen gegeben werden.

fg
hannes

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo HannesB,

Möglichkeit 1 ist gut. Natürlich entsteht ein gewisser (Codierungs)Aufwand, aber den hast du ja in den anderen Fällen auch ... nur eben ohne die Nachteile der anderen Möglichkeiten. Aus dieser Sicht ganz klar 1.

Hallo Pria,

ich weiß nicht genau, wie du deine Vorschläge meinst, aber ich vermute, dass "ungetestet" hier genau das Problem ist. Alle mir bekannten Möglichkeiten des Durchreichens außer auf jeder Ebene das Event neu zu definieren, das der darunterliegende Ebene zu abonnieren und dann das eigenen Event die nächsthöhere Ebene neu auszulösen, bringen irgendwelche Probleme mit sich.

herbivore

D
201 Beiträge seit 2007
vor 13 Jahren

Hallo,

ich würde in dem Fall wahrscheinlich Möglichkeit 2 wählen. Habe das auch in einer Anwendung mal so gemacht und es funktioniert gut.
In meinem Fall gibt es eine zentrale Stelle, die informiert wird, wenn an Objekten im Projekt eine bestimmte Änderung passiert. Diese (statische) Klasse bietet das dann als event an. Jede Komponente, die sich dafür "interessiert" abonniert das event.
Gruß

HannesB Themenstarter:in
185 Beiträge seit 2005
vor 13 Jahren

Danke für eure Antworten,

ich habe es (nach Unterhaltung und als Vorschlag eines Arbeitskollegen) nun folgendermaßen implementiert (Code nachbearbeitet):


internal class Importer<T>
{
  private ComponentWithEvent Component_A { get; set; }
  
  public event EventHandler<MyEventArgs> MainClassEvent
  {
      add
      {
          Component_A.SubEvent += value;
      }
      remove
      {
          Component_A.SubEvent -= value;
      }
  }

Die Klasse "Importer", welche von einem Importprozess verwendet wird, stellt also das Event bereit, beim Zugriff auf dieses wird jedoch direkt an das Event der Komponente gebunden. Ist also eine Abwandlung von Möglichkeit 1, mit dem Vorteil, dass das Event nicht doppelt definiert werden muss.

@DFDotNet: Wie löst du das Problem, dass diese zentrale Instanz dann ALLE Events bekommt? Wenn es z.B. mehrere Instanzen des "Importers" gibt, schicken diese alle ihre Events an die zentrale Instanz. Beim Loggen dieser Meldungen bekommt man dann ev. mehr Meldungen, als man sich wünscht. 😉
Lösung wäre, dass man den Instanzen eine Id gibt, daimt man wieder unterscheiden kann, was jedoch auch ein Aufwand ist, da diese Id u.a. an die Subkomponenten weitergegeben werden müsste usw.

thx
hannes

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo HannesB,

Ist also eine Abwandlung von Möglichkeit 1, mit dem Vorteil, dass das Event nicht doppelt definiert werden muss.

... und dem Nachteil, dass dem Abonnenten ein völlig unbekannter Sender übergeben wird. Das ist in meinen Augen ein eklatanter Nachteil, um nicht zu sagen Fehler.

Bleibe bei der originalen Möglichkeit 1 und definiere die Events sauber neu.

herbivore

HannesB Themenstarter:in
185 Beiträge seit 2005
vor 13 Jahren

Hallo herbivore,

danke für deinen hinweis!
ich habe es nun so abgeändert, dass es Möglichkeit 1 entspricht.

fg
hannes

D
201 Beiträge seit 2007
vor 13 Jahren

@DFDotNet: Wie löst du das Problem, dass diese zentrale Instanz dann ALLE Events bekommt? Wenn es z.B. mehrere Instanzen des "Importers" gibt, schicken diese alle ihre Events an die zentrale Instanz. Beim Loggen dieser Meldungen bekommt man dann ev. mehr Meldungen, als man sich wünscht. 😉
Lösung wäre, dass man den Instanzen eine Id gibt, daimt man wieder unterscheiden kann, was jedoch auch ein Aufwand ist, da diese Id u.a. an die Subkomponenten weitergegeben werden müsste usw.

Das Problem stellt sich bei mir nicht. Es ist so, dass bei einer bestimmten Änderung an einem Objekt u.U. andere Komponenten die Anzeige aktualisieren müssen o.ä.. Deshalb senden alle Objekte die Info an den 'ChangeNotifier'. Die Komponenten, die dessen event wiederum abbonieren, entschieden anhand der Infos in den event-Arguments (enthalten eine Referenz auf das objekt), ob es für sie relevant ist oder nicht. Aber erstmal bekommen sie natürlich das event von allen Objekten mit, auch wenn es eventuell irrelevant ist.

U
282 Beiträge seit 2008
vor 13 Jahren

Nur als Stichwort: Methode 2 und 3 erinnern mich an Signals aus Qt

L
667 Beiträge seit 2004
vor 13 Jahren

Ich sehe eigentlich keine Unschönheit bei Möglichkeit 1 - Das entspricht doch genau dem umgekehrten Weg z.B. bei einer Fassade :

Klasse A ruft B.TuWas() auf, Klasse B ruft C.TuWas() auf usw.

Wenn im Fall Deines Events Die Komponenten A, B Aggregate der Importer-Klasse sind, dann "gehört" deren Funktionalität nunmal zum Toolset der Importer-Klasse mit dazu. Wenn es also für die Funktion des Importers eine Rolle spielt, ein Event nach oben zu melden, dessen Auftreten über das Aggregat A oder B festgestellt wird, dann gehört das Event in die Schnittstelle der Importer-Klasse einfach rein.

Anders sieht es imho aus, wenn so ein Event aus Klasse A logisch gesehen nichts mit der Funktionalität der Importer-Klasse zu tun hat. Dann ist ein Redesign nötig.

"It is not wise to be wise" - Sun Tzu