+4 Daumen
127 Aufrufe

MVC.png 
1. Einführung

Im Gegensatz zu den Design-Patterns, die nur einzelne Teile von Software-Problemen betreffen, geht es bei den Architekturmustern um die Gesamtsysteme. Jedes bewährte Architekturmuster (davon gibt es nur etwa 5 bis 10) erfüllt einen ganz bestimmten Anwendungszweck. Als langlebige Konzepte sind sie besonders hilfreich bei der Entwicklung von Anwendungen, deren Benutzungsdauer lange in die Zukunft reicht. Für die Datenerhaltung einer Bank ist z. B. wichtig, dass nicht jeder dieselben Daten sehen kann (Rollenkonzept). Die in diesem Artikel behandelte MVC-Architektur ist für Benutzerflächen ausgelegt und ihr Einsatzzweck liegt in dialogorientieren Anwendungen mit vielen wechselnden Benutzern. Sie lässt sich sowohl in objektorientierten, als auch funktionalen Programmiersprachen verwirklichen. Der Grundgedanke fußt auf der Idee, Zuständigkeiten zu trennen und jedem Bestandteil eine bestimmte Aufgabe zuzuweisen. Die UI wird vom Rest der Anwendung losgelöst. Logik und Daten erfahren ebenfalls eine radikale Trennung. Das gesamte System wird dadurch in zwei Teile gesägt: die Dialogführung und das Model. Ähnlich wie ein Baumstamm, der in der Mitte durchgesägt wurde, lässt sich damit nur bedingt sinnvoll arbeiten und aus diesem Grund wird die Dialogführung noch einmal in die Komponenten Controller und View unterteilt. Das Model ist dabei alles, was nichts mit dem UI zu tun hat. Die Verarbeitung der Daten wird dem Controller überlassen, der mit Events (wie z.B. Tastatureingaben) klarkommen muss. Aus der Sicht der MVC-Anwendung haben wir nun die Richtung "systemeinwärts" betrachtet. In die Richtung "systemauswärts" gibt es eine oder mehrere Views, die Daten in irgendeiner Form nach außen präsentieren. Die verschiedenen Teilnehmer an der Anwendung besitzen jeweils eigene Peripheriegeräte zur Systemkommunikation. Das geordnete Paar \(\left(\text{View},\text{Controller}\right)\) existiert daher für jeden Nutzer, was für die Architektur mehrere unabhängige Dialogkomponenten, die dabei verschiedene Implementierungen haben können, bedeutet. Es wird sichergestellt, dass der Controller Daten aus den Events aufnehmen kann. Die Art und Weise wie dieser Prozess durchgeführt wird, spielt dabei jedoch keine Rolle. Das Model besitzt im Gegensatz zu den Dialogkomponenten nur eine einzige Implementierung.

2. Konkrete architektonische Umsetzung (Implementierung)

Im Sinne einer feingranularen Modularisierung für getrennte Zuständigkeiten gemäß dem Grundsatz "Separation of Concerns", weist man einzelnen Bestandteilen bestimmte Zuständigkeiten zu. Die Idee dahinter ist, dass man die ganzen Daten in einem separaten Teil, dem sogenannten Datastore, das sich ähnlich einer Datenbank um die gesamte Datenerhaltung kümmert, speichert. Die Business-Logic findet in einer vom Datastore getrennten Komponente (der sog. Logic) Platz. Logic und Datastore bilden gemeinsam das Model der MVC-Architektur. Die für Code und Algorithmen zuständige Logic speichert nichts, wohingegen die Datastore-Komponente einzig und allein für die Persistierung der Daten zuständig ist. Die Logic Komponente kann jederzeit gekillt und neugestartet werden. Der Controller übersetzt ein Event in eine Business-Operation (z.B. eine Überweisung). Diese Operation geht anschließend durch einen Methodenaufruf an die Logic, die beim Datastore nachfragen muss, ob die an der Überweisung beteiligten Kunden bei ihm hinterlegt sind. Der Zugriff auf das Datastore ist dem fehlenden Wissen der Logic-Komponente über die Daten im System geschuldet. Sind die Daten (Kontostände, Kunden etc.) valide, so manipuliert die Logic (und nur die Logic) das Datastore. Anschließend sendet die Logic das notifyObservers raus und die Observer erfahren durch das Datastore ein Update.

3. Die Datastore-Komponente

Datastore ist passiv, d.h. wenn es nicht von außen angesteuert wird, handelt es nicht. Sie weiß außerdem nichts von der Logic, was sie unabhängig von der Ablaufsteuerung und Darstellung macht. Bis auf die Views, welche die Rolle der Observer innehaben, kennt das Datastore keine anderen Komponenten im System. Die Logic verlässt sich darauf, dass das Datastore, welches den Zustand der gesamten Anwendung speichert, nur sinnvolle Informationen enthält. Die Art der Speicherung (Datenbank, XML, ...) ist ebenfalls austauschbar ohne dass das System nach außen eine Veränderung zeigt (bis auf die Einflüsse, die der gewählte Persistenzmechanismus auf die Performance hat). Dies zeigt die Verborgenheit der technischen Speicherstruktur. Das Datastore ist mit einem Singleton vergleichbar, von dem nur eine Instanz existieren darf.

4. Die Logic-Komponente

Die Logic führt als identitätslose Komponente des MVC-Architekturmusters Business-Operations durch. Das sind höhere Operationen mit komplexeren Abläufen, die mitunter sehr aufwendig sein können und aus einzelnen Operationen zusammengesetzt sein können. Dazu zählen z.B. viele Getter, die eine konsistente Abbildung von einem ursprünglichen Datenzustand in einen "neuen" Datenzustand repräsentieren. Die Logic selbst ist dabei (wie das HTTP-Protokoll) zustandslos, da sie keine Daten besitzt und ohne Datastore demnach nicht lebensfähig ist. Sie kann jedoch alle lesenden und schreibenden Operationen des Datastores nutzen, um die durch eine Business-Operation erforderlichen Datenmodifikationen durchzuführen, wobei am Ende ein konsistenter Systemzustand gewährleistet sein muss. Die einzige Referenz der Logic sollte die auf das Datastore sein. Deshalb kann sie auch beliebig getauscht werden. Für den/die Controller stellt sie außerdem logische Operationen zur Verfügung, kennt sie (die Controller) allerdings nicht. Auch von den Views weiß die Logic nichts. Wie bereits erwähnt kann die Logic ohne das Vorhandensein eines Datastore nicht sinnvoll existieren. Diese beiden Komponenten sollten an einem Strang ziehen und ein "Vertrauensverhältnis" haben. D. h. das Datastore hinterfragt nicht, ob das, was die Logic macht, auch sinnvoll ist. Die von den Controllern genutzten, höheren Operationen werden von der Logic z. B. in Form eines Interfaces zur Verfügung gestellt. Sie liefert außerdem Daten nach der Geschäftslogik gefiltert für die Views (nicht an die Views, da sie sich nicht für solche interessiert). Wenn überhaupt Daten in der Logic vorhanden sind, dann handelt es sich um gecachte Daten. Dabei besitzt sie allerdings keine Datenhoheit. Wie bereits angesprochen, spielt das Observer-Pattern in einer MVC-Architektur-basierten Anwendung eine entscheidende Rolle. Die Logic-Komponente fungiert als eine Art Meta-Observer, da es sich selbst auch als Observer beim Datastore anmelden kann, um seine Caches zu aktualisieren. Primärdaten besitzt jedoch nur das Datastore.

5. Der Controller

Die Dialogführung wird durch die Controller und die View implementiert. Bis auf die Logic muss der Controller nichts kennen. Er hat keinen Zugriff auf die View, da man beim Zusammenschweißen von zwei getrennten Komponenten an Flexibilität verliert. Das Datastore ist ihm ebenfalls unbekannt. Man kann den Controller als eine Art Compiler für Business-Operationen auffassen, da er externe Events/Dialogereignisse interpretiert und in logische (Business-)Operationen übersetzt. Alle Controller sehen dieselbe Logic und können sich im laufenden Betrieb sowohl an-, als auch abmelden wann sie wollen. Von den Controllern, die sich untereinander nicht kennen, kann es mehrere Implementierungen innerhalb der dialogorientierten Anwendung geben.

6. Die View

Die View implementiert die Darstellung. Sie ist ein Observer im Observer-Pattern und kennt nur das Datastore (Observable), bei dem es sich anmeldet. Sie sehen jedoch nur einen Teil des Datastores und haben nur lesenden Zugriff in Form von Gettern. Auch hier gilt wieder: sind mehrere Views vorhanden, kennen diese sich untereinander nicht. Werden sie über ein Update von ihrem Observable informiert, holen sich die Views ihre Daten, die sie zur Aktualisierung der Darstellung benötigen. In einigen Fällen können die Views z.B. zum Anzeigen der Operationen auch die Logic kennen.

7. Das Zusammenspiel von Controller und View

Betrachten wir eine UI mit Dialogelementen, erkennt man die direkte Kopplung der Controller- und View-Komponente. Beide können auch isoliert sinnvoll verwendet werden. Im Wesentlichen kann man unterscheiden zwischen

- Controller ohne View:

Solche Controller sind autonome Roboter, die aufgrund einer ihr innewohnenden Algorithmik Aktionen, wie z.B. eine Datenbereinigung in der Nacht, anstoßen. Es gibt keine Personen, die dabei zuschauen oder in Form eines System-User-Dialogs zur Mitarbeit am autonom arbeitenden System genötigt werden.

- Views ohne Controller

Das sind Zuschauer, die beispielsweise einem spannenden Schachspiel am Bildschirm oder Kämpfen von NPCs in Videospielen beiwohnen. Die Logic sorgt dafür, dass die Spielregeln eingehalten werden und die Datastore-Komponente ist dann das Spielbrett. Die Beobachter haben keinen Einfluss auf das System.

Setzt man die Architektur um, sollte man regelmäßig prüfen, ob alle Abhängigkeiten noch korrekt eingehalten sind. In Java können sie durch Imports überprüft werden. Es gibt eine Vielzahl an Tools, die Dependenzgraphen erstellen, anhand derer man in komplexen Anwendungen nicht den Überblick bei der Abhängigkeitsanalyse verliert.

Autor: Florian André Dalwigk

geschlossen: Stack-Artikel
von
Das Mitglied hat durch den Artikel 50 Bonuspunkte erhalten. Schreib auch du einen Artikel.


Ich konnte den Hinweis nicht mehr oben einfügen, da die max. 12.000 Zeichen erreicht wurden ;-)

Ein anderes Problem?

Stell deine Frage

Willkommen bei der Stacklounge! Stell deine Frage sofort und kostenfrei

x
Made by a lovely community
...