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?
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
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.
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
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
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
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
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ß
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
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
Hallo herbivore,
danke für deinen hinweis!
ich habe es nun so abgeändert, dass es Möglichkeit 1 entspricht.
fg
hannes
@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.
Nur als Stichwort: Methode 2 und 3 erinnern mich an Signals aus Qt
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