2017-01-04 2 views
1

In meiner Anwendung (MVVM-Architektur basierte WPF-Anwendung) verwende ich eine Menge ICommand s als Dienste. Einige dieser Befehle können an Menüelemente, Symbolleisten, Statusleisten usw. gebunden werden, wo sie in die entsprechenden Containeransichtsmodelle eingefügt werden. Nun sind einige von ihnen zum Beispiel in der Lage, Daten auf der Shell der Anwendung zu manipulieren, so dass die Shell eine Abhängigkeit von ihnen ist. Da die Shell auch diese Container hostet (Menü, Statusleiste ...), bekomme ich eine zirkuläre Abhängigkeit. (Shell -> Menü -> Befehl -> Shell).Circular Abhängigkeiten und Inversion der Kontrolle - wie diese zu lösen?

Derzeit verwende ich MEF, um meine Anwendung zu verfassen, so dass die meiste Zeit das Problem durch Eigentum oder private Mitgliederinjektion gelöst werden kann, aber ich habe das Gefühl, dass dies eine Art üble Praxis ist (ein Dienst hat keine Möglichkeit) einem Elternteil mitteilen, dass er diese Abhängigkeit benötigt, obwohl dies der Fall ist).

Meine Frage ist: Was ist ein gemeinsamer Weg ist ein Problem wie dieses zu lösen:

class Shell : IShell 
    .ctor(IMenu) 

class Menu : IMenu 
    .ctor(ICommand[]) 

class ExitCommand : ICommand 
    .ctor(IShell) 
+0

Zwei gängige Methoden, um einen Befehl von einem übergeordneten Ansichtsmodell ('Shell') aufzurufen oder global verfügbare Befehle zu haben, sind' EventAggregator' -Implementierungen ('PubSubEvent' in' Prism') oder 'CompositeCommand' (wieder' Prism')). – mechanic

+0

Was ist die "Shell"? Könntest du es mehr beschreiben? – Joe

+0

@Joe Die Shell ist hauptsächlich ein Ansichtsmodell, das als Container für alle anderen Ansichtskomponenten dient. – artganify

Antwort

1

Ich habe MEF nie benutzt, aber eine Abhängigkeit Injection-Container verwenden und haben ähnliche Probleme konfrontiert. Das Problem ist, (denke ich), dass Sie Ihre Shell als Service (mit der Funktionalität des Beendens) herumwerfen, aber es fungiert auch als ein ViewModel (Menüs anzeigen usw.). Es übernimmt mehr Verantwortung, als es sollte.

Lassen Sie uns Ihre catchall „iShell“ Schnittstelle in separate Module aufgeteilt, dass demonstrieren:

class Shell : IShell, IExitManager 
    .ctor(IMenu) 

class Menu : IMenu 
    .ctor(ICommand[]) 

class ExitCommand : ICommand 
    .ctor(IExitManager) 

Ihre Schale zwei Dinge auf einmal tut, dann ist es Ansichtsmodell stuff (iShell) und es ist die Verwaltung verlass (IExitManager).

Was ich tun würde, ist die Funktionalität von Ihrem ViewModel zu abstrahieren. Ich würde einen dedizierten IExitManager Service erstellen. Anstatt die ShellViewModel diese Funktionalität zu implementieren und das Ganze an Orten einzufügen, die nur ein Exit-Ereignis auslösen müssen (Ziehen der Menüfunktionalität und Herbeiführen zirkulärer Abhängigkeiten), haben Sie stattdessen diese Funktionalität in einem dedizierten IExitManager.

Statt der aktuellen:

enter image description here

Statt den Dienst bringen aus der Shell-Implementierung:

enter image description here

Ihre Klasse IExitManager müssen die gleiche Funktionalität wie Sie zur Zeit aussetzen habe in IShell (ich vermute eine Exit() Methode) und eine Eventhandler ExitRequestedHandler für Ihre Shell zu hören und durchzuführen es ist Aktion.

Ein EventAggregator ist im Grunde eine allgemeinere Version von diesem - ich würde Ihnen empfehlen, einen Blick darauf zu werfen. Sie könnten Ihre Shell auf EventExit-Ereignisse warten lassen und Ihren Befehl ausgeben. Die einzige allgemeine Abhängigkeit ist der Event Aggregator-Dienst. Was diese Lösung macht, ist eine "einmalige" Version nur für Exit-Ereignisse. Wenn Sie dies häufig tun, verwenden Sie einen EventAggregator.

Verwandte Themen