MVC (Model-View-Controller )
“Everyone interprets MVC or MVP slightly different. ” (Thomas Weidenfeller )
Model-View-Controller (kurz „MVC “) ist eine Bauweise von Programmen, die auf jederzeit mögliche Benutzereingaben reagieren können sollen. MVC wurde um 1978 bei Xerox entwickelt. Viele Programme verwenden heute MVC oder eine Variante dieser Bauweise.
In MVC wird eine Anwendungskomponente in drei Teile zerlegt, nämlich in das oberflächenunabhängige “Model ”, das für die Ausgabe zuständige “View ” und den für die Interpretation von Eingabeereignissen zuständigen “Controller ”. Wegen der Dreiheit spricht man auch von einer „MVC-Triade “.
Man beachte, daß das englische Wort “controller ” mit zwei L geschrieben wird, während das englische Wort “model ” nur mit einem L geschrieben wird, das deutsche Wort „Modell“ aber wieder mit zwei L.
Der “Controller ” ist ein Ereignisinterpretierer (wörtlich soviel wie eine „Steuerungseinheit“), das “Model ” der „Anwendungskern“ und das “View ” der „Darsteller“ (wörtlich soviel wie eine „Ansicht“). Ein View zusammen mit seinem Controller wird hier auch als eine Oberfläche bezeichnet.
In dem Diagramm „Der Fluss von Informationen bei MVC “ wird dargestellt, wie eine Benutzereingabe vom Controller in Operationen des Model und des View umgesetzt wird. So kann eine Eingabe für den Benutzer dann eine sichtbare Wirkung durch eine veränderte Anzeige haben.
Der Fluss von Informationen bei MVC
Eingabe Ausgabe
Oberflaeche Oberflaeche
.-"""-. .-"""-. .-"""-. .-"""-. .-"""-.
.` `. .` `. .` `. .` `. .` `.
/ \ / \ / \ / \ / \
; Benutzer ;-->; Controller;-->; Model ;-->; View ;-->; Benutzer ;
\ / \ / \ / \ / \ /
'. .' '. .' '. .' '. .' '. .'
'-----' '-----' '-----' '-----' '-----'
| ^
'-------------------------------'
Sobald man eine GUI -Bibliothek der Umgebung verwendet (wie z.B. Swing ) wird diese zur Ausgabe verwendet, und sie gibt auch die Benutzereingaben an das Programm weiter. Damit repräsentiert die GUI-Bibliothek praktisch für das Programm den Benutzer, so daß sie in dem Diagramm „Der Fluss von Informationen bei MVC mit GUI-Bibliothek “ den Benutzer ersetzt.
Der Fluss von Informationen bei MVC mit GUI-Bibliothek
Ereignisse Ausgabe
Oberflaeche Oberflaeche
.-"""-. .-"""-. .-"""-. .-"""-. .-"""-.
.` `. .` `. .` `. .` `. .` `.
/ \ / \ / \ / \ / \
; GUI ;-->; Controller;-->; Model ;-->; View ;-->; GUI ;
\ / \ / \ / \ / \ /
'. .' '. .' '. .' '. .' '. .'
'-----' '-----' '-----' '-----' '-----'
| ^
'-------------------------------'
Das View legt zusammen mit dem Controller das Erscheinungsbild und Interaktionsverhalten einer Anwendungskomponente fest. Daher können beide zusammen auch als die Oberflächenkomponente oder Oberfläche angesehen werden. (Sie treten gemeinsam manchmal als „Oberflächenbeauftragter“ (UI delegate ) auf, wenn von einer weiteren Abstraktionsschicht zur Implementation eines variablen Erscheinungsbildes und Interaktionsverhaltens an sie delegiert wird). Die Oberfläche einer Anwendungskomponente stellt deren Schnittstelle zur GUI-Bibliothek und damit indirekt auch zum Benutzer dar.
Ein Model und seine Oberflaeche
.-----------------------------------------------------------.
| Benutzer |
| |
'-----------------------------^-----------------------------'
|
.-----------------------------v-----------------------------.
| GUI-Bibliothek |
| |
'-----------------------------------------------------------'
| ^ Darstellung
Ereignisse |
| |
.------------|---------------------------------|------------.
| GUI-Ober- v Beobachtung | |
| flaeche .-"""-. .-"""-. |
| .` `. .` `. |
| / \ / \ |
| ;Controller ;-------------------->; View ; |
| \ / Aenderung \ / |
| '. .' _'. .' |
| '-----' \ /|'-----' |
| \ / Beobachtung |
'--------------------\------------------/-------------------'
Aenderung \ Ereignisse
\ /
\| .-"""-. /
--.` `./
/ \
; Model ;
\ /
'. .'
'-----'
Das Model
Das Model repräsentiert die Zustände und Operationen der Anwendung. Das Model weiß nichts von Controllern und Views. Es
- stellt aber Operationen bereit, die dann vom Controller zur Umsetzung von Ereignissen genutzt werden können, es
- stellt Zustandsinformationen bereit, die dann vom View abgerufen werden können, um sie darzustellen und es
- akzeptiert Anmeldungen von Ereignisempfängern und benachrichtigt diese über Änderungen seiner Zustände. Das wird vom View benutzt, um bei Änderungen die Darstellung aktualisieren zu können.
Das Anwendungs-Model (application model ) kann selber noch einen inneren Kern, das Fach-Model (domain model ), enthalten. Während das Anwendungs-Model spezifische Operationen und Zustände einer bestimmten Anwendungskomponente repräsentiert, enthält das Fach-Model nur Operationen und Informationen eines bestimmten Fachgebiets, die selber aber nicht speziell auf eine bestimmte Anwendung zugeschnitten wurden. Da die Oberfläche aber direkt nur auf das Anwendungs-Model zugreift, wird im folgenden diese innere Model -Struktur meist nicht weiter betrachtet. Sie sollte aber natürlich dann berücksichtigt werden, wenn tatsächlich ein Model programmiert werden soll. Für die Oberfläche (und damit auch für das ganze MVC-System) ist das Model zunächst immer der Anwendungskern: Sie sieht eine eventuellen inneres Fach-Model nicht direkt.
Mehrere Oberflächen zu einem Model
Zu einem Model kann es auch mehrere Oberfläche geben kann, die nichts voneinander wissen. Wenn eine Oberfläche den Zustand des Model verändert, dann müssen alle anderen Oberflächen mit demselben Model davon erfahren, damit sie die Ausgabe entsprechend der Änderung aktualisieren können. So könnte eine Oberfläche einen Zustand des Model als Zahl darstellen und eine andere denselben Zustand durch eine Farbe. Wenn der Zustand dann durch eine Eingabe verändert wird, müssen die Ausgaben beider Views sofort angepaßt werden, falls sie von der Änderung betroffen sind. Dazu ruft ein Controller zunächst eine Veränderungsoperation des Model auf. Das Model benachrichtigt dann alle angemeldeten Beobachter, also beide Views, von dieser Änderung. Den benachrichtigten Views obliegt es alsdann die Änderung in der Darstellung wiederzugeben.
Ändert der im Schaubild dargestellte »Controller 0« etwas am Model, so müssen unter Umständen beide Views, also das »View 0« und das »View 1«, davon erfahren.
Bei einer Model-Aenderung durch eine Oberflaeche werden beide Views informiert
Ereignisse Ausgabe
.-"""-. Oberflaeche 0 .-"""-.
.` `. .` `.
/ \ / \
;Controller ;---------------------; View ;
/\ 0 /# #\ 0 /\
.-"""-. / '. .' # .-"""-. # '. .' \ .-"""-.
.` `. / '-----' # .` `. # '-----' \ .` `.
/ \/ #/ \# \/ \
; GUI ; ; Model ; ; GUI ;
\ /\ /\ /# /\ /
'. .' \ -"""- / '. .' # -"""- / '. .'
'-----' \ .` `. / '-----' # .` `. / '-----'
\/ \/ #/ \/
;Controller ;---------------------; View ;
\ 1 / \ 1 /
'. .' '. .'
'-----' Oberflaeche 1 '-----'
Manchmal wird MVC dahingehend mißverstanden, daß das Hinzufügen mehrerer Oberflächen zu einem Modell als „Sinn“ von MVC angesehen wird. Tatsächlich ist es aber hilfreich, wie MVC es erlaubt, Quellcode übersichtlich zu gliedern und Verantwortlichkeiten eindeutig zuzuordnen, auch wenn von der Möglichkeit, mehrere Oberflächen zu einem Modell zu fügen, gar kein Gebrauch gemacht wird.
Beobachter (“Observer ”)
Ein beobachtbares Objekt sendet Informationen über gewisse Veränderungen seines Zustandes an Beobachter (“observer ” oder “dependents ”), ohne daß diese dem Objekt statisch bekannt sind. Die Beobachter sind also nicht im Quellcode des beobachteten Objektes festgelegt. Daher sagt man auch, sie seien „entkoppelt“ oder „lose angebunden“, denn der Quellcode ist dadurch vollkommen unabhängig von Veränderungen der Beobachter.
Ein Beobachter beantragt bei einem zu beobachtenden Objekt erst zur Laufzeit zunächst die Zusendung von Änderungsnachrichten (»update« oder »update: aParameter«). Das beobachtbare Objekt merkt sich dann gegebenenfalls den Beobachter als Abonnenten von Änderungsnachrichten. Ein beobachtbares Objekt kann mehrere Beobachter akzeptieren. Beobachter können sich auch wieder abmelden, wenn sie keine Nachrichten mehr erhalten wollen.
Bei einer Änderung sendet das beobachtbare Objekt sich selbst eine Änderungsnachricht (»changed« oder »changed: aParameter«), was dann zum Senden der Änderungsnachricht (»update« oder »update: aParameter«) an die Beobachter führt.
Ein eventueller Parameter solch einer Nachricht enthält nicht etwa den geänderten Zustand, sondern kann nur verwendet werden, um bei Bedarf genauer einzugrenzen, welcher Teil des Zustandes sich verändert hat. Will ein Beobachter den neuen Zustand erfahren, kann er alsdann eine weiter Nachricht zu diesem Zweck an das beobachtbare Objekt schicken.
In MVC werden Views und eventuell auch Controller als Beobachter an das Model gebunden. Außerdem ist der Controller gewissermaßen auch wieder ein Beobachter von GUI -Elementen.
View
Ein View stellt einige oder alle Aspekte des Zustandes eines Model dar. Außerdem baut es meist GUI-Bedienkomponenten auf, mit denen der Benutzer Eingaben (Nachrichten) erzeugen kann. Die Art und Anzahl dieser GUI-Komponenten kann auch vom Zustand des Model abhängen.
Das View kennt seinen Controller und auch sein Model. Er kann Informationen aus dem Model abrufen, um sie darzustellen. Um zu wissen, wann er diese aktualisieren sollte, kann er sich beim Model als Ereignisempfänger (Beobachter) anmelden. Das View erhält dann bei Änderungen Ereignisse vom Model. Man spricht hier von einer losen Ankopplung des Views an das Model, weil das Model nicht weiß, daß es ein View gibt, sondern lediglich einen An- und Abmeldedienst für Ereignisempfänger bereitstellt und auf Anfrage Auskünfte zu seinem Zustand erteilt. Dadurch muß das Model bei Änderungen der Oberfläche nicht angepaßt werden und ist insofern von der Oberfläche entkoppelt.
Eine Änderung der Anzeige kann auch direkt vom Controller angestoßen werden.
Der Controller
Die vom View dargestellten GUI-Komponenten machen Informationen über den Zustand von Model und View für den Benutzer nicht einfach nur sichtbar, sondern erlauben es ihm auch oft, durch Bedienung dieser Komponenten Nachrichten an das Programm zu schicken.
The View displays the Model on the screen. View provides a simple protocol to pass information to and from the Model. The heart of a View object presents the Model data in one particular way that is of interest to the end user. Different views may support the same data, i.e., the same Models, in completely different ways. The classic example is that one View may show data as a bar graph while another shows the same data as a pie chart.
Der Controller interpretiert solche GUI-Ereignisse der Oberfläche, die der zum ihm gehörende Views erzeugt hat, durch Operationen des Model und des Views, welche ihm beide bekannt sind. Er erzeugt die Views .
Die Anwendungsoperationen sollten nicht im Controller sondern im Model implementiert werden. Der Controller kann als dünner Adapter zwischen GUI-Ereignissen und den Operationen des Anwendungs-Model und des Views angesehen werden. Er ordnet nur zu, welches Ereignis welche Operationen auslöst.
Der Controller kann auch ein Beobachter des Models sein, falls sein Verhalten vom Model -Zustand abhängen soll. Diese Möglichkeit wird zur Vereinfachung im Rest dieses Textes nicht behandelt.
Der Aufbau der Oberfläche
Die drei Komponenten der MVC -Triade werden zunächst außerhalb der Triade erzeugt. Das ist schon deshalb nötig, weil manchmal eben ein Model mit mehreren Oberflächen verbunden werden soll, so daß es falsch wäre, nach einem einheitlichen Schema etwa stets ein neues Model zu erzeugen. Dabei wird der Controller von seinem Model informiert.
Das View implementiert traditionell die Initialisierungsoperation zum Herstellen der nötigen Verbindung der drei Komponenten. Dazu wird diese Operation mit Angabe einer Referenz auf die beiden anderen Komponenten Controller und Model von einem Haupt-Controller aufgerufen. Außerdem erhält das View dabei auch Referenzen auf die benötigten Teile der Umgebung (GUI-Oberfläche), wie beispielsweise gegebenenfalls einen Behälter, in dem es etwas darstellen soll.
Das View
- meldet sich daraufhin beim Model als Beobachter an, um über alle für ihn relevanten Änderungen von Model -Zuständen benachrichtig zu werden,
- merkt sich die Referenz auf seinen Controller,
- meldet sich als View bei seinem Controller an, damit der Controller sich eine Referenz auf sein View merken kann und
- baut dann unter Verwendung der GUI-Bibliothek und der Umgebung die Oberfläche in der GUI-Umgebung auf, wobei es den ihm bekannten Controller als Ereignisempfänger in diejenigen Oberflächenkomponenten einträgt, welche Ereignisse erzeugen.
Hierbei wird vorausgesetzt, daß der Controller sein Model bereits kennt, weil er schon vor diesen Schritten außerhalb der Triade entsprechend eingestellt wurde.
Der ganze Prozeß wird durch zu Beginn eines Programms durch eine Startnachricht »open« an einen Haupt-Controller (“top-level controller ”) angestoßen. Dieses ist der Controller für das Hauptfenster des Programms.
Damit werden also folgende Operationen der Komponenten vorausgesetzt.
Operationen des Views
Schnittstelle fuer die Aussenwelt
- model:controller:( GUI-Umgebung, Model, Controller )Operationen des Model
Schnittstelle fuer Beobachter
- akzeptieren( Beobachter )Operationen des Controllers
Schnittstelle fuer View:
- akzeptieren( model )
- anmelden( View )
Zusätzliche Operationen eines Haupt-Controllers:
- open()
- terminate()
Die Initialisierung des Views kann auch in dessen Konstruktor erfolgen, wird dadurch aber mit der Erzeugung des Views gekoppelt, was nicht immer erwünscht sein muß.
Der Abbau der Oberfläche
Beim Abbau der Oberfläche meldet sich das View vom Model und vom Controller ab und veranlaßt den Abbau aller zuvor aufgebauten Oberflächenkomponenten der GUI-Bibliothek.
Eine eventuell notwendige Abmeldung des Model vom Controller sollte außerhalb der Triade erfolgen.
Damit werden also insgesamt folgende Operationen der Komponenten vorausgesetzt.
Operationen des Views
Schnittstelle fuer die Aussenwelt
- model:controller:( GUI-Umgebung, Model, Controller )
- release()Operationen des Model
Schnittstelle fuer Beobachter
- anmelden( Beobachter )
- abmelden( Beobachter )Operationen des Controllers
Schnittstelle fuer das View:
- akzeptieren( Model )
- akzeptieren( View )
- abmeldenView()
- abmeldenModel()
Besondere Schnittstelle eine Haupt-Controllers:
- open()
- terminate()
Der Betrieb der Triade
Im Betrieb der Triade werden dann noch folgende Operationen verwendet.
- Bei einer Änderung eines Zustandes des Model wird das View vom Model benachrichtigt, falls er sich zuvor für Nachrichten über Änderungen dieses Zustandes angemeldet hat. Dabei wird nur mitgeteilt, daß sich dieser Zustand verändert hat, aber nicht wie.
- Falls das View von der Veränderung eines Zustandes erfahren hat, dann kann er Auskunftsoperationen des Model aufrufen, um den neuen Zustand zu ermitteln.
- Der Controller Ereignisses der GUI-Oberfläche, die das View zuvor aufgebaut hat, empfangen und umsetzen können.
- Model und View bieten dem Controller Handlungsoperationen an, die dieser aufrufen kann, um Ereignisse umzusetzen.
In der folgenden Liste der Operationen der Komponenten der Triade werden die Operationen mit drei Punkten geschrieben, die mehrfach auftreten können. So könnte das View beispielsweise zwei Operationen für Model -Änderungen bieten, nämlich die Operation »update:model:with-A« und die Operation »update:model:with-B«, wofür in der folgenden Aufstellung dann der Text »update:model:with« geschrieben wird.
Operationen des Views
Schnittstelle fuer die Aussenwelt
- va3 model:controller:( GUI-Umgebung, Model, Controller )
- vr3 release()
Schnittstelle fuer das Model
- vpe update:model:with()
Schnittstelle fuer den Controller
- vda Handlung ... vollziehen( ... )Operationen des Model
Schnittstelle fuer Beobachter
- mao observer:( observer )
- mro release( observer )
- mtv Auskunft ... erteilen( ... )
Schnittstelle fuer den Controller:
- mda Handlung ... vollziehen( ... )
interne Schnittstelle:
- mfe changed:with( ... ) /* --> "update:self:with" to dependents */Operationen des Controller
Schnittstelle fuer das View:
- cav view:view( view )
- crv release()
Schnittstelle fuer die Aussenwelt:
- cam anmelden( model )
- crm abmeldenModel()
Schnittstelle fuer die GUI-Bibliothek:
- cpe event()
Besondere Schnittstelle eine Haupt-Controllers:
- cto open()
- ctt terminate() /* "controlTerminate" */
In manchen Fällen werden nicht alle der hier genannten Operationen benötigt. Sie müssen dann natürlich auch nicht unbedingt alle implementiert werden.
Die Kürzel sollen es ermöglichen, Methoden in Programmen kurz damit zu kennzeichnet. Der zweite Buchstabe steht für ein Verb “accept ”, “release ”, “process ”, “do ”, “tell ”, “fire ” oder “top-level ”, der dritte für “3 objects ”, “event ”, “action ”, “observer ”, “value ”, “event ”, “view ”, “model ”, “open ” oder “terminate ”.