GUI-(Architektur)-Pattern

https://oer-informatik.de/gui_pattern_mvc

tl/dr; (ca. 12 min Lesezeit): Benutzeroberflächen werden häufig in drei Bestandteile aufgeteilt: die Datenhaltungsschicht (Model), die Geschäftslogik (Controller) und die Presentationsschicht (View). Ausgehend von dieser Dreiteilung wagen wir einen Blick auf das GUI-Pattern MVC, dessen Herkunft, die zentralen Eigenschaften, die alle Interpretationen des Patterns gemein haben. Ist überall MVC drin, wo MVC draufsteht?

(Dieser Artikel ist Bestandteil einer Serie von Artikeln zu GUI-Pattern, in der MVC, MVVM und MVP voneinander abgegrenzt werden.)

Gemeinsamkeiten der meisten GUI-Pattern

Mit der Programmiersprache Smalltalk wurde 1979 erstmals ein Architekturmuster eingeführt, das die Geschäftslogik und die Präsentationsschicht als komplett eigenständige Komponenten kapselte. Diese Trennung folgt dem Designprinzip “Seperation of Concerns” (SoC). Die Abstraktion der Datenstruktur der Problemdomäne (Model) wird getrennt von der Präsentationsschicht (View + Controller-Paar). Martin Fowler1 spricht hier von Separated Presentation und zieht parallelen dieser Entwicklung etwa zu den UNIX-Befehlen, für die es die Separated Presentations Kommandozeile (command line interface, CLI) und graphische Oberfläche (graphical user interface, GUI) gibt.

Heutige GUI-Pattern bauen häufig auf dem bei Smalltalk eingeführten Model-View-Controller (MVC)-Muster auf. Dieses kennt in der ursprünglichen Version drei Rollen: das Model für die Daten/Persistenz, der Controller für die Business-Logik und die View für die eigentliche Darstellung.

Die Aufteilung in drei Komponenten hat sich in den verschiedenen Spielarten des MVC-Musters erhalten, jedoch gibt es in den konkreten Ausprägungen dieser GUI-Muster in verschiedenen Frameworks große Unterschiede:

  • Die Granularität: gibt es für eine “Seite” jeweils Model, View und Controller oder für jede Seitenkomponente (z.B. Textbox)?

  • Die Verantwortlichkeiten: Welche Aufgaben sind in den jeweiligen Rollen enthalten? Wer ist z.B. für Validierung und für die Geschäftslogik verantwortlich?

  • Die Abhängigkeiten: Wie sind die Beziehungen der drei Rollen zueinander: wer hält Referenzen auf welche andere Rolle?

  • Die Kommunikation: Wer benachrichtigt wen bei Aktualisierungen? Wer verarbeitet die Anfragen der Benutzer*innen?

Die Aufteilung in Model / View / Controller (Presenter / ViewModel)

Das Modell (englische Schreibweise: Model)

Das Model regelt und kapselt den Zugriff und die Verarbeitung der Daten der Problemdomäne und hält den Zustand, der diese Daten repräsentiert. Nur der Teil der Geschäftslogik, der direkt mit der Speicherung, Änderung, Löschung oder Erzeugung von Daten zusammenhängt, liegt im Verantwortungsbereich des Models, ebenso wie Operationen, die unmittelbar auf dem Model ausgeführt werden müssen (z.B. die Berechnung von abgeleiteten Attributen wie z.B. Durchschnitten oder anderen derivied values).

Idealerweise ist jedes Model für sich abgeschlossen und eigenständig: Änderungen eines Models sollten möglichst keine anderen Model betreffen. Das Model liefert auf Anfrage den Zustand der Daten und reagiert auf Befehle diesen zu ändern.

Das Model darf weder vom Controller noch von der View direkt abhängen. Controller verfügen über direkten Zugriff (eine Referenz) auf das Model und aktualisieren dieses.

Die Kommunikation mit der View erfolgt indirekt: Entweder wird über den Controller kommuniziert (z.B. im MVP-Pattern, hier heißt der Controller jedoch Presenter ) oder es wird beispielsweise über das Observer-Pattern eine lose Kopplung zwischen Model und View realisiert: Views registrieren sich als Observer bei den Models. Im Falle einer Aktualisierung benachrichtigen die Models alle registrierten Views über Änderungen.

Bei dem Modell selbst kann es sich wiederum um mehrere Einzelkomponenten handeln: die Datenstruktur wird häufig mit einfachen Entitäts-Klassen (in Java “POJOs”: plain old Java objects) abgebildet, für komplexere Geschäftslogik und die Logik zur Datenspeicherung (Repositories und PersistenceUnits) werden gemäß des Single Responsibility Principles weitere Klassen implementiert.

Die View

Die View ist verantwortlich für die Präsentation der Daten eines Models. Es ist möglich, dass unterschiedliche Views die selben Daten unterschiedlich darstellen. Das selbe Model kann unter Umständen als Liste, Tabelle, Formular oder Diagramm dargestellt werden oder für unterschiedliche Endgeräte (als Website, DesktopApp oder für andere Geräte).

Zwischen View und Model bestehen keine direkten Abhängigkeiten, sie sind höchstens über Interfaces lose gekoppelt (Observer-Pattern). Durch diese Entkopplung wird erreicht, dass die Komponenten leicht testbar, veränderbar (ETC: easy to change) und austauschbar sind.

Die unterschiedlichen Realisierungen unterscheiden sich darin, in wie weit in der View bereits Geschäftslogik enthalten ist (z.B. Validierung, Zusammenstellung von AJAX-Requests usw.).

Der Controller

Der Controller kommuniziert sowohl mit dem Model als auch mit der View und stellt das Bindeglied zwischen den beiden dar.

Er nimmt die Anfragen des Benutzers entgegen (teilweise mittelbar über die View), gibt Datenänderungen an das Model weiter. Er ruft also unmittelbar die Getter- und Setter-Methoden des Models auf.

Er wählt die korrekte View aus, stellt die nötigen Daten zusammen und sendet alles an den anfragenden Client oder aktualisiert die jeweilige Komponente der View.

Jede GUI-Komponente (Widget, also jede Textbox, jeder Button usw.) kann über ein eigenes View/Controller-Paar verfügen.

Nachrichtenfolge im MVC-Pattern

Ein typischer Nachrichtenablauf wäre etwa der folgende:

  • Ein Benutzer gibt in einer GUI sein Geburtsdatum ein und drückt “Speichern”

  • Der Controller erhält die Nachricht mit den neuen Werten und führt die benötigte Logik aus (hier: ruft die Setter-Funktion des Model auf)

  • Am Model ist die View über das Observer-Pattern angemeldet. In der Setter-Methode ist festgelegt, dass alle Beobachter informiert werden sollen, sobald ein Wert sich ändert - daher wird die View über neue vorliegende Werte informiert

  • Die View holt sich die neuen Werte beim Model ab und stellt sie dar.

Vorteile und umgesetzte Prinzipien

  • Separation of Concerns (SoC): die Schichten View, Controller und Model werden unterschiedlich häufig, aus unterschiedlichen Gründen und ggf. von unterschiedlichen Abteilungen geändert. Eine klare Trennung der Concerns sorgt für verständlichen Code, der nicht mit Bestandteilen mehrerer Concerns verunreinigt ist.

  • Der Code bleibt so easy to change (ETC): die View wird in der Regel häufiger geändert als das Model – oftmals auch durch unterschiedliche Abteilungen. Diese Änderungen wirken sich nur auf definierte Codeabschnitte aus.

  • Der Code ist leichter testbar, da die unterschiedlichen Concerns separat getestet werden können – evtl. Abhängigkeiten können durch die klare Trennung und Vermeidung von direkten Abhängigkeiten über Mocking eingebunden werden.

  • Das Prinzip des DRY wird gewährleistet: Die Codeabschnitte zum Model können ggf. von unterschiedlichen Views wiederverwendet werden.

Jede Komponente auf einer Benutzeroberfläche (ein sogenanntes Widget: ein “Window-Gadget”) kann über ein eigenes Paar aus View und Controller verfügen - also etwa jede Textbox, jeder Radiobutton usw. Darüber hinaus gibt es oft für die Gesamtheit aller Komponenten ein View /Controller-Paar.

Gemeinsam haben die meisten GUI-Pattern die Aufteilung in die drei Bestandteile Model, View und Controller. Sie unterscheiden sich jedoch stark in den Vorgaben, in welchem Umfang diese Komponenten Geschäftslogik realisieren und wie sie miteinander kommunizieren.

Die Theorie

Martin Fowler2 umreist die wesentlichen Eigenschaften von MVC folgendermaßen:

  • Make a strong separation between presentation (view & controller) and domain (model) - Separated Presentation.
  • Divide GUI widgets into a controller (for reacting to user stimulus) and view (for displaying the state of the model). Controller and view should (mostly) not communicate directly but through the model.
  • Have views (and controllers) observe the model to allow multiple widgets to update without needed to communicate directly - Observer Synchronization.

Probleme bei MVC

Aus den Rollen der MVC-Komponenten ergeben sich einige Sonderfälle, mit denen MVC nur schwer umgehen kann:

Wohin mit Business-Logik, die zur Darstellung der View nötig ist (Validierung, Internationalisierung usw.)?

Wenn die Darstellung der View in Abhängigkeit von Zuständen des Modells variieren soll (z.B. bei Überschreitung bestimmter Werte) müsste die View Geschäftslogik enthalten, die eigentlich Bestandteil der Problemdomäne ist. Wo sollte die dann sinnvoll untergebracht werden?

  • Im Controller - als Ort für die (übergeordnete) Geschäftslogik,

  • im Model, da nur dort die benötigten Zustände bekannt sind - und hier die das Model direkt betreffende Geschäftslogik steht,

  • oder in der View, da es schließlich um Fragen der Darstellung geht?

Wo speichere ich Zustände der View, die nicht Teil der Problemdomäne (also des Models) sind?

In manchen Anwendungsfällen ist es wichtig, Komponenten der View einen Zustand zuzusprechen: ausgegraute Buttons, vorausgewählte Einträge, übergeordnete Informationen. Auch hier stellt sich die Frage, ob es Aufgabe der View ist, diesen Zustand vorzuhalten - oder ob es im Model gespeichert werden sollte.

Lösung mit Application Model / Presentation Model

Das Ausgangsproblem war:

  1. Zusätzlich zu den Attributen der Problemdomäne (des Models) müssen also noch andere (View-abhängige) Zustände gespeichert werden.

  2. Zusätzlich zur Geschäftslogik der Problemdomäne muss auch die View-abhängige Geschäftslogik erfasst werden.

Eine mögliche Lösung ist, zwischen Model und View noch eine Zwischenschicht zu setzt, die den Zugriff auf die Attribute des Models ergänzt um die zusätzliche nötigen View-Zustände (Attribute) und Geschäftslogiken (Methoden). Diese Zwischenschicht wird häufig Application Model oder Presentation Model genannt. Sie ist mit der View über das Oberserver-Pattern verknüpft - die Anbindung des eigentlichen Models erfolgt nur mittelbar über diese Zwischenschicht (beispielsweise über das DesignPattern Adapter).

Unterschiedliche Ausprägungen und Weiterentwicklungen von MVC heute

Die ursprüngliche in Smalltalk umgesetzte Idee, dass für jedes Widget einer Application ein eigenes MVC-Triple existiert ist heute in Frameworks nicht mehr anzutreffen. Statt dessen haben sich andere Weiterentwicklungen des MVC-Patterns etabliert:

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/design-pattern/gui-pattern.

Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag


  1. https://martinfowler.com/eaaDev/uiArchs.html#ModelViewController

  2. https://martinfowler.com/eaaDev/uiArchs.html#ModelViewController

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: