Implementierung von Software nach dem Model View ViewModel Entwurfsmuster

Entwurfsmuster spielen im Bereich der Softwareentwicklung eine große Rolle. Sie stellen für wiederkehrende Entwurfsprobleme in der Softwarearchitektur eine Vorlage zur Problemlösung dar. Ziel der Entwurfsmuster ist es dabei, durch ein vorgefertigtes Template eine effizientere Implementierung von Software zu ermöglichen.

Man unterscheidet zwischen zahlreichen verschiedenen Entwurfsmustern. Im Bereich der Entwurfsmuster für grafische Benutzerschnittstellen (GUI) sind beispielsweise Model View Controller (MVC), Model View Presenter (MVP) und Model View ViewModel (MVVM) als wichtige Vertreter zu nennen. Im Rahmen dieses Artikels möchte ich dir das MVVM-Entwurfsmuster erläutern und darauf eingehen welche Vorzüge sich durch eine nach MVVM entworfene Architektur ergeben.

Erstmals wurde das Model View ViewModel Entwurfsmuster 2005 von John Gossman veröffentlicht. Ursprünglich zielte es auf die Erstellung von User Interfaces (UI) mittels der Plattform Windows Presentation Foundation (WPF) ab. Das zentrale Ziel von MVVM ist es, eine Trennung der Darstellung von der Geschäfts- und Präsentationslogik einer Anwendung zu erreichen. Hierzu wird die Software unterteilt in die drei Komponenten Model, View und ViewModel.

Im Folgenden betrachten wir die Zuständigkeiten der drei Komponenten am Beispiel einer einfachen MVVM-Anwendung. In dieser soll der vollständige Name eines Nutzers basierend auf den Nutzereingaben von Vor- und Nachname zusammengesetzt und schließlich in der View angezeigt werden. Für das in diesem Artikel gezeigte Codebeispiel wurden C# und WPF genutzt.

Das Model

Beim Model handelt es sich um ein Klassenobjekt, das die Geschäftslogik und die für die Darstellung in der View benötigten Daten bereitstellt. Durch Nutzereingaben können diese Daten verändert werden.

class User 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
}

Das ViewModel

Das ViewModel stellt das Bindeglied zwischen View und Model dar, es enthält die Präsentationslogik. Hierbei besitzt das ViewModel jedoch keinerlei Kenntnis der View. Um dennoch den Datenfluss zur View zu ermöglichen, stellt das ViewModel öffentliche Eigenschaften und Befehle zur Verfügung. Diese werden von der View mittels Datenbindung an Steuerelemente gekoppelt und definieren somit die durch die Benutzeroberfläche zur Verfügung gestellte Funktionalität.

Im folgenden Beispiel eines ViewModels bewirkt eine Änderung der FirstName-Property auch eine Anpassung von FullName. Um die View bezüglich der Änderung von FullName zu benachrichtigen, implementiert das ViewModel (indirekt über BaseViewModel) das Interface INotifyPropertyChanged. Durch das PropertyChanged-Event wird die View über eine Änderung der via Datenbindung gekoppelten Property informiert.

public class ViewModel : BaseViewModel
{
    private User user;

    public ViewModel()
    {
        user = new User
        {
            FirstName = "Max",
            LastName = "Mustermann"
        };
    }

    public string FirstName
    {
        get { return user.FirstName; }
        set
        {
            if(user.FirstName != value)
            {
                user.FirstName = value;
                RaisePropertyChanged(nameof(FirstName));
                RaisePropertyChanged(nameof(FullName));
            }
        }
    }

    public string LastName
    {
        get { return user.LastName; }
        set
        {
            if(user.LastName != value)
            {
                user.LastName = value;
                RaisePropertyChanged(nameof(LastName));
                RaisePropertyChanged(nameof(FullName));
            }
        }
    }

    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }
}
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void RaisePropertyChanged( string propertyName = "")
    {
        if (!string.IsNullOrEmpty(propertyName))
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Die View

Die View ist für die Anzeige von Inhalten auf der Benutzeroberfläche verantwortlich. Beispielsweise werden in der View die Bedienelemente definiert, die dem Nutzer des UI zur Verfügung stehen. Außerdem werden in der View Layout und Erscheinungsbild der grafischen Benutzerschnittstelle bestimmt. Hierbei wird die View in XAML (eXtensible Application Markup Language) implementiert. Wenn das MVVM-Entwurfsmuster streng umgesetzt wird, sollte der zugehörige Code-Behind keine Logik beinhalten.

<Window.DataContext>
        <local:ViewModel/>
</Window.DataContext>
<Grid Margin="0,0,0,0">
    <Label Content="Vorname: " HorizontalAlignment="Left" Margin="17,21,0,0" VerticalAlignment="Top"/>
    <Label Content="Nachname: " HorizontalAlignment="Left" Margin="17,52,0,0" VerticalAlignment="Top"/>
    <!-- Die Text-Property wird via Datenbindung an die Property FirstName des ViewModels gekoppelt -->
    <TextBox HorizontalAlignment="Left" Height="23" Margin="106,24,0,0" TextWrapping="Wrap" 
             Text="{Binding FirstName}" VerticalAlignment="Top" Width="120"/>
    <!-- Die Text-Property wird via Datenbindung an die Property LastName des ViewModels gekoppelt -->
    <TextBox HorizontalAlignment="Left" Height="23" Margin="106,55,0,0" TextWrapping="Wrap" 
             Text="{Binding LastName}" VerticalAlignment="Top" Width="120"/>
    <!-- Die Content-Property wird via Datenbindung an die Property FullName des ViewModels gekoppelt -->
    <Label Content="{Binding FullName, Mode=OneWay}" HorizontalAlignment="Left" Margin="106,104,0,0" 
           VerticalAlignment="Top" Width="120"/>
</Grid>

Code-Behind:

public partial class View : Window
{
    public View()
    {
        InitializeComponent();
    }
}

Grafische Benutzeroberfläche:

Fazit

Die Implementierung von Software nach dem Model View ViewModel Entwurfsmuster bietet viele Vorteile. Beispielsweise resultiert die Beschränkung der UI-Logik auf das ViewModel in einer guten Testbarkeit. So kann das ViewModel unabhängig von der View instanziiert und durch Komponententests automatisiert getestet werden. Da die View selbst keine Logik beinhaltet, entfällt die Notwendigkeit von UI-Tests. Die Entkopplung der View sorgt außerdem dafür, dass man diese jederzeit austauschen kann, ohne dass Änderungen am ViewModel vorgenommen werden müssen. Zudem lässt sich die Entwicklungsarbeit durch die klare Trennung der drei Komponenten gut parallelisieren. Dies ermöglicht außerdem eine effiziente Arbeitsteilung basierend auf den Fähigkeiten des Entwicklungsteams. Beispielsweise kann ein Designer am Layout der Benutzeroberfläche arbeiten, während die Entwickler Model und ViewModel implementieren.

Allerdings sollte die Implementierung nach MVVM gut geplant sein. Zunächst erfordert das MVVM Muster meist einen höheren Entwicklungsaufwand. Außerdem muss man davon ausgehen, dass es durch den bi-direktionalen Datenbindungsmechanismus zu Einbußen in der Performanz kommt.