EventAggregator mittels IOC Containers

Seit einiger Zeit lese ich verschiedene Open-Source Projekte. Ich möchte wissen wie andere Entwickler spezielle Probleme lösen oder welche Architektur sie dabei verwenden. Mein Ziel ist es, die Projekte so weit zu verstehen, dass eine Mitarbeit für mich möglich ist. Ich übe damit die selbstständige Einarbeitung, was einen positiven Nebeneffekt für den Kunde bietet.

Es ist bekannt, viele Wege führen nach Rom. Bei der Softwareentwicklung wird immer davon gesprochen, bestimmte Probleme mittels Software zu lösen. Ähnlich wie bei der Metapher gibt es unterschiedliche Ansätze. Ich habe häufig beobachtet, dass sich für die einfachste/schnellste Variante entschieden wird. Um von vorneherein eine bessere Entscheidung zu treffen, ist es sinnvoll, sich Open-Source-Projekte anzusehen. Diese können Lösungen oder bekannte Probleme enthalten und somit eine bessere Planung ermöglichen.

Ich möchte damit nicht zum Abschreiben ermutigen, ich meine nur, das Rad muss nicht jedes Mal neu erfunden werden. Oft wird versucht, sehr spezielle Prozesse abzubilden, dadurch werden Standard-Lösungen unmöglich. Dies wiederum treibt die Kosten für Entwicklung und Wartungsaufwand in die Höhe. Wird sich aus Gründen für eine eigene Entwicklung entschieden, empfehle ich, dennoch bestehende Lösungen zu analysieren, um mögliche Fehlentscheidungen gleich im Vorhinein zu eliminieren.

Aber zurück zum eigentlichen Thema. Durch das lesen, lassen sich einige interessante Patterns finden. Die ich besonders spannend finde, möchte ich in diesem Beitag vorstellen. Um den folgenden Text zu verstehen, sollten die folgenden Themen bekannt sein. Martin Fowler - Event Aggregator Martin Fowler - Inversion of Control Containers

Der normale EventAggregator bietet eine Publish- sowie Subscribe-Funktion an. In einer Komponente befindet sich der Code, um ein Subscribe aufzurufen. Je nach Clean-Code-Verständnis kann es unterschiedliche Schreibweisen geben. Wobei ich bei dieser Auswahl die zweite Schreibweise bevorzugen würde, da diese mehr Beschreibung durch den Methoden-Namen bringt. Oft findet man diese direkt im Konstruktor oder werden gesammelt in einer Methode Ala WireEvents (wird auch manchmal sehr lang).

_eventAggregator.Subscribe<UserRoleChanged>(() => {
    ...
    ValidateUserRights()
    ...
});
_eventAggregator.Subscribe<UserRoleChanged>(() => ValidateUserRights());

Auf der anderen Seite findet ein publish des Events statt.

_eventAggregator.Publish(new UserRoleChanged(){
    oldUserRole: UserRole.SuperUser,
    newUserRole: UserRole.User
});

Bei meiner Recherche bin ich auf eine andere Variante gestoßen.

Der Publish-Teil bleibt gleich, die Subscription-Seite ist anders. Lets talk code…

public interface IHandle<T> {
    void Handle(T arg);
}
public class UserRoleViewModel : IHandle<UserRoleChanged> {
    ...
    public void Handle(UserRoleChanged arg){
        ValidateUserRights()
    }
    ...
}

Der Subscribe erfolgt über das implementieren eines IHandle<T>-Interfaces, der Generic Type definiert das Event. Durch das Interface muss eine Handle-Methode implementiert werden, diese wird ausgeführt, sobald das Event mit publish aufgerufen wird.

Wie dies funktioniert, zeigt die Pseudo implementierung der EventAggregator.Publish Funktion.

public class EventAggregator : IEventAggregator {
    ...
    public void Publish<T>(T args) {
        var subscribers = _ioc.ResolveAll<IHandle<T>>();
        subscribers.foreach(x => x.Handle(args));
    }
    ...
}

In einer Gegenüberstellung sind mir folgende Vor- und Nachteile aufgefallen. Gerne erweitere ich diese Liste bei Vorschlägen.

Beim subscriben von Events ist weniger Code notwendig, eine WireEvents-Funktion entfällt 👍 ( + )

Kleinerer Konstruktor. Gleichzeitig ein Nachteil, da dieses verhalten der Klasse von außen nicht anzusehen ist 👍 ( + ) 👎 ( - )

Beim unit testing wird weniger oder kein Code für ein EventAggregator benötigt, da sich die Funktionen aufgrund des Interfaces direkt aufrufen lassen 👍 ( + )

Kein Wenn-Dann gesteuertes Lauschen möglich, dies geschieht ständig 👎 ( - )

Es wird ein IOC-Container gebraucht 👎 ( - )

Kein Standard Implementierung, selbst implementiert notwendig, für dritte schwer Verständlich 👎 ( - )

Auf den ersten Blick sieht es so aus, als würden die Nachteile überwiegen. Ist jedoch von Projekt zu Projekt unterschiedlich, da bereits einen IOC-Container oder eine eigene Implementierung des EventAggregator existiert. Ich finde die Argumente weniger Code zuschreiben sehr wichtig, daher könnte ich mir gut vorstellen, diese Implementierung mal auszuprobieren. Schließlich bedeutet: weniger Code = weniger Fehler