Wo zum speichern von Programm-Einstellungen/Status in einer MVVM-Anwendung
Ich experimentiere mit MVVM für die erste Zeit und wirklich wie die Trennung von Verantwortlichkeiten. Natürlich zu jedem design pattern erst löst viele Probleme - nicht alle. So versuche ich herauszufinden, wo Sie speichern den Zustand der Anwendung und den Speicherort für Anwendungs-Breite Befehle.
Lässt sagen meiner Anwendung eine Verbindung zu einer bestimmten URL. Ich habe eine ConnectionWindow und ein ConnectionViewModel unterstützen das sammeln dieser Informationen aus der Benutzer und die Berufung auf Befehle, um eine Verbindung zu der Adresse. Das nächste mal die Anwendung gestartet wird, möchte ich wieder zu diesem gleichen Adresse ohne Aufforderung des Anwenders aus.
Meine Lösung bisher ist die Erstellung einer ApplicationViewModel liefert einen Befehl, um die Verbindung zu einer bestimmten Adresse, und speichern Sie diese Adresse, um einige persistente Speicherung (wo es eigentlich gespeichert ist irrelevant für diese Frage). Unten ist eine gekürzte Klasse Modell.
Wird in der application view model:
public class ApplicationViewModel : INotifyPropertyChanged
{
public Uri Address{ get; set; }
public void ConnectTo( Uri address )
{
//Connect to the address
//Save the addres in persistent storage for later re-use
Address = address;
}
...
}
Verbindung anzeigen Modell:
public class ConnectionViewModel : INotifyPropertyChanged
{
private ApplicationViewModel _appModel;
public ConnectionViewModel( ApplicationViewModel model )
{
_appModel = model;
}
public ICommand ConnectCmd
{
get
{
if( _connectCmd == null )
{
_connectCmd = new LambdaCommand(
p => _appModel.ConnectTo( Address ),
p => Address != null
);
}
return _connectCmd;
}
}
public Uri Address{ get; set; }
...
}
Die Frage ist also: Ist ein ApplicationViewModel der richtige Weg, dies zu behandeln? Wie sonst könnte Sie die store-Anwendung, Staat?
EDIT: ich würde gerne auch wissen, wie diese auf die Testbarkeit. Einer der primären Gründe für den Einsatz von MVVM ist die Fähigkeit zum testen der Modelle ohne eine host-Anwendung. Ich speziell bin daran interessiert, Einblick in die Funktionsweise zentrale app-Einstellungen wirken sich auf Testbarkeit und die Fähigkeit zu improvisieren aus den abhängigen Modellen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn du nicht mit M-V-VM, die Lösung ist einfach: Sie stellen diese Daten und Funktionalität in Ihrer Anwendung abgeleiteten Typ. - Anwendung.Aktuelle haben Sie dann Zugriff darauf. Das problem ist hier, wie Sie wissen, ist, dass die Anwendung.Aktuelle Probleme verursacht, wenn die unit-Tests das ViewModel. Das ist, was behoben werden muss. Der erste Schritt ist, zu entkoppeln uns von einer konkreten Instanz der Anwendung. Tun Sie dies durch die Definition einer Schnittstelle und der Umsetzung der auf die konkrete Anwendung geben.
Nun der nächste Schritt ist die Beseitigung der Aufruf von Application.Strömung innerhalb der ViewModel durch die Verwendung von Inversion of Control oder Service Locator.
Alle von der "globalen" Funktion wird nun durch ein mockable service-interface IApplication. Du bist immer noch mit der linken, wie zu konstruieren das ViewModel mit dem richtigen service-Instanz, aber es klingt wie Sie bereits den Umgang? Wenn Sie auf der Suche für eine Lösung gibt, Onyx (disclaimer, ich bin der Autor) kann eine Lösung gibt. Ihre Anwendung würde den Unkenrufen.Das Ereignis Created und fügen sich als ein service und der Rahmen würde sich auf den rest.
Ich generell ein schlechtes Gefühl bekommen über code, der einen Blick Modell die direkte Kommunikation mit anderen. Ich mag die Idee, dass die VVM Teil des Musters sollte grundsätzlich steckbar und nichts in diesem Bereich der code sollte, hängt von der Existenz von irgendetwas anderem in diesem Abschnitt. Die Argumentation hinter diesem ist, dass ohne eine Zentralisierung der Logik kann es schwierig werden, zu definieren, Verantwortung.
Auf der anderen Seite, basierend auf Ihre tatsächlichen code, kann es auch einfach sein, dass die ApplicationViewModel ist schlecht benannt, es nicht machen eine Modell zugänglich auf einen Blick, so kann dies einfach eine schlechte Wahl der Namen.
Entweder Weg, die Lösung kommt zu einem brechen der Verantwortung. So wie ich das sehe, haben Sie drei Dinge zu erreichen:
Ich würde vorschlagen, dass Sie drei Klassen statt Ihrer zwei.
Das nächste, was zu entscheiden ist, wie die Adresse bekommt der SettingsProvider. Man könnte es passieren, von der ConnectionViewModel wie Sie tun derzeit, aber ich bin nicht so scharf auf, weil es erhöht die Kopplung von view-Modell, und es ist nicht die Verantwortung des ViewModel zu wissen, dass es muss durchhalten. Eine weitere option ist, um den Anruf aus der ServiceProvider, aber es hat nicht wirklich das Gefühl, zu mir, wie es sein sollte der Dienstleister die Verantwortung. In der Tat fühlt es sich nicht wie jemand, der Verantwortung anderer als der SettingsProvider. Das führt mich zu glauben, dass die Einstellung für die Anbieter hören sich für änderungen an der verbundenen Adresse und dem persistieren, ohne intervention. In anderen Worten ein Ereignis:
Damit wird die enge Kopplung zwischen dem ServiceProvider und dem SettingsProvider, die Sie vermeiden wollen, wenn möglich, und ich würde verwenden einen EventAggregator hier, die ich besprochen habe in einer Antwort auf diese Frage
Zur Adresse die Probleme der Testbarkeit, haben Sie jetzt eine sehr definierte Erwartung für das, was jede Methode tun. Die ConnectionViewModel rufen wird eine Verbindung, Die ServiceProvider zu verbinden und die SettingsProvider bestehen bleiben. Zum testen der ConnectionViewModel werden Sie wahrscheinlich wollen, zu konvertieren, die Kopplung an die ServiceProvider, von einer Klasse zu einem interface:
Dann können Sie mit einem mocking-framework einzuführen, verspottet IServiceProvider, dass Sie können überprüfen, um sicherzustellen, dass die connect-Methode aufgerufen wurde, mit den erwarteten Parametern.
Testen der anderen beiden Klassen ist schwieriger, da Sie darauf angewiesen, dass ein richtiger server und real-persistent-storage-Gerät. Sie können mehr hinzufügen, Ebenen der Dereferenzierung zu verzögern (zum Beispiel einem PersistenceProvider, dass die SettingsProvider verwendet), aber schließlich verlassen Sie die Welt von unit-Tests und geben Sie den Integrationstests. In der Regel, wenn ich code mit dem Muster über die Modelle und Modelle kann eine gute unit test-Abdeckung, aber die Anbieter erfordern komplizierte Testmethoden.
Natürlich, wenn Sie einen EventAggregator zu brechen, Kupplung und IOC, um Tests zu vereinfachen, ist es wahrscheinlich lohnt ein Blick in eines der dependency injection-frameworks wie Microsoft Prism, aber auch wenn Sie zu spät sind entlang in der Entwicklung zu re-Architekten eine Menge von Regeln und Muster können angewendet werden, um bestehenden code in eine einfachere Möglichkeit.
Ja, Sie sind auf dem richtigen Weg. Wenn Sie zwei controls in Ihrem system, die miteinander kommunizieren müssen Daten, die Sie wollen, es zu tun in einer Weise, die so entkoppelt wie möglich. Es gibt mehrere Möglichkeiten, dies zu tun.
In PRISMA 2 Sie haben einen Bereich, der wie eine Art von "Daten-bus". Eine Kontrolle könnte produzieren Daten mit einem Schlüssel, der Hinzugefügt wird, auf den bus, und jedes Steuerelement, das will, die Daten können sich ein callback wenn die Daten ändert.
Ich persönlich implementiert haben etwas nenne ich "ApplicationState". Es hat den gleichen Zweck. Es implementiert die INotifyPropertyChanged, und jemand in das system schreiben kann, die bestimmte Eigenschaften oder abonnieren Sie Termine ändern. Es ist weniger allgemein als die Prism-Lösung, aber es funktioniert. Das ist ziemlich viel, was Sie erstellt haben.
Aber nun haben Sie das problem, wie zu übergeben, um den Zustand der Anwendung. Die alte Schule Weg, dies zu tun, ist es ein Singleton. Ich bin kein großer fan von diesem. Stattdessen habe ich ein interface definiert als:
Jede visuelle Komponente in der Struktur kann diese Schnittstelle implementieren, und übergeben Sie einfach den Status der Anwendung auf das ViewModel.
Dann in das root-Fenster, wenn das Loaded-Ereignis ausgelöst wird, ich durchqueren die visuelle Struktur und Aussehen für Steuerelemente, die möchten, dass der app-Zustand (IApplicationStateConsumer). Ich gebe Ihnen die appState, und mein system ist initialisiert. Es ist eine poor-man ' s dependency injection.
Auf der anderen Seite, Prism löst alle diese Probleme. Ich wünschte, ich könnte gehen zurück und re-Architekt mit Prism... aber es ist ein bisschen zu spät für mich, um kosteneffektiv zu sein.