Laufen FxCop auf meinem Code, bekomme ich diese Warnung:
Microsoft.Maintainability: 'FooBar.ctor ist mit 99 gekoppelt verschiedene Arten von 9 verschiedenen Namespaces. Schreiben oder Refaktorieren der Methode, um seine Klassenkopplung zu verringern, oder erwäge, die Methode auf eins zu verschieben von den anderen Typen ist es eng zusammen mit. Eine Klassenkopplung oben 40 zeigt schlechte Wartbarkeit an, a Klassenkopplung zwischen 40 und 30 zeigt mäßige Wartbarkeit an, und eine Klassenkopplung unter 30 weist auf gute Wartbarkeit hin.
Meine Klasse ist eine Landezone für alle Nachrichten vom Server. Der Server kann uns Nachrichten verschiedener EventArgs-Typen senden:
%Vor%Die Methoden "HandleSignOut" und "HandleConnectionTest" enthalten wenig Code. Üblicherweise geben sie die Arbeit an eine Funktion in einer anderen Klasse weiter.
Wie kann ich diese Klasse mit geringerer Kopplung besser machen?
Lassen Sie die Klassen, die die Arbeit machen, sich für Ereignisse registrieren, an denen sie interessiert sind ... an Ereignisbroker Muster.
%Vor%Sie können auch ein IoC-Framework wie Spring.NET verwenden, um das Wörterbuch zu injizieren. Auf diese Weise müssen Sie, wenn Sie einen neuen Nachrichtentyp erhalten, diesen zentralen Hub nicht neu kompilieren - ändern Sie einfach eine Konfigurationsdatei.
Das lang erwartete Beispiel:Erstellen Sie eine neue Konsolenanwendung namens Beispiel und fügen Sie Folgendes hinzu:
%Vor%Und eine app.config-Datei:
%Vor%Ausgabe:
%Vor% Wie Sie sehen, kennt MessageBroker
nichts von den Handlern, und die Handler wissen nichts von MessageBroker
. Die gesamte Zuordnung erfolgt in der Datei app.config. Wenn Sie einen neuen Ereignistyp verarbeiten müssen, können Sie ihn in der Konfigurationsdatei hinzufügen. Das ist besonders schön, wenn andere Teams Ereignistypen und Handler definieren - sie können ihre Daten einfach in einer DLL kompilieren, sie in Ihre Implementierung einfügen und einfach ein Mapping hinzufügen.
Das Dictionary hat Werte vom Typ object anstelle von MessageHandler<>
, weil die eigentlichen Handler nicht in MessageHandler<EventArgs>
umgewandelt werden können, also musste ich etwas herumhacken. Ich denke, dass die Lösung immer noch sauber ist und Mapping-Fehler gut verarbeitet werden. Beachten Sie, dass Sie in diesem Projekt auch auf Spring.Core.dll verweisen müssen. Sie finden die Bibliotheken hier und die Dokumentation hier . Das Kapitel zur Abhängigkeitsinjektion ist dafür relevant. Beachten Sie auch, dass Sie dafür keinen Spring.NET verwenden müssen - die wichtige Idee hier ist die Abhängigkeitsinjektion. Irgendwie muss etwas dem Broker sagen, dass er Nachrichten vom Typ a an x senden soll, und die Verwendung eines IoC-Containers für die Dependency-Injection ist eine gute Möglichkeit, dass der Broker nichts über x weiß und umgekehrt.
Einige andere SO-Fragen im Zusammenhang mit IoC und DI:
Ich sehe den Rest Ihres Codes nicht, aber ich würde versuchen, eine viel kleinere Anzahl von Event-Arg-Klassen zu erstellen. Erstellen Sie stattdessen ein paar, die in Bezug auf die enthaltenen Daten und / oder die Art und Weise, wie Sie sie später behandeln, ähnlich sind, und fügen Sie ein Feld hinzu, das Ihnen sagt, welcher genaue Ereignistyp aufgetreten ist (wahrscheinlich sollten Sie eine Aufzählung verwenden).
Idealerweise würden Sie diesen Konstruktor nicht nur viel lesbarer machen, sondern auch die Art und Weise, wie die Nachrichten behandelt werden (Gruppennachrichten, die in einer einzelnen Ereignisbehandlungsroutine in ähnlicher Weise behandelt werden)
Verwenden Sie vielleicht, anstatt für jede Nachricht eine andere Klasse zu verwenden, ein Flag, das die Nachricht identifiziert.
Das würde die Anzahl der Nachrichten drastisch reduzieren und die Wartbarkeit erhöhen. Meine Vermutung ist, dass die meisten Nachrichtenklassen ungefähr null Unterschied haben.
Es ist schwer, eine zusätzliche Art des Angriffs zu wählen, weil der Rest der Architektur (mir) unbekannt ist.
Wenn Sie sich beispielsweise Windows ansehen, weiß er nicht, wie er mit jeder Nachricht umzugehen hat, über die möglicherweise gesprochen wird. Stattdessen registrieren die zugrunde liegenden Nachrichtenhandler Rückruffunktionen mit dem Hauptthread.
Sie könnten einen ähnlichen Ansatz verfolgen. Jede Nachrichtenklasse muss wissen, wie sie mit sich selbst umgehen soll und sich bei der größeren Anwendung registrieren kann. Dies sollte den Code erheblich vereinfachen und die enge Kopplung beseitigen.
Offensichtlich benötigen Sie einen Dispatcher-Mechanismus: Abhängig von dem Ereignis, das Sie erhalten, möchten Sie einen anderen Code ausführen.
Sie scheinen das Typsystem zu verwenden, um die Ereignisse zu identifizieren, während es eigentlich Polymorphie unterstützen soll. Wie Chris Lively vorschlägt, könnte man genauso gut (ohne das Typsystem zu missbrauchen) eine Aufzählung verwenden, um die Nachrichten zu identifizieren.
Oder Sie können die Leistungsfähigkeit des Typsystems nutzen und ein Registrierungsobjekt erstellen, in dem jeder Ereignistyp registriert wird (durch eine statische Instanz, eine Konfigurationsdatei oder was auch immer). Dann könnten Sie das Muster der Verantwortungskette verwenden, um den richtigen Handler zu finden. Entweder führt der Handler die Verarbeitung selbst aus, oder er kann eine Factory sein und ein Objekt erstellen, das das Ereignis behandelt.
Die letztere Methode sieht ein bisschen unterspezifiziert und überentwickelt aus, aber im Fall von 99 Ereignistypen (bereits) erscheint es mir angemessen.
Tags und Links design-patterns design coupling