MVC oder was?
Don P
- programmiertechnik
Hallo SELFler,
Irgendwie habe ich Probleme, das Model-View-Controller-Muster umzusetzen. Das grobe Prinzip, wie bei Wikipedia beschrieben, ist mir einigermaßen verständlich, aber wie setzt man sowas konkret um?
Hier mal ein konstruiertes Beispiel: In einem Restaurant gebe es
– Beliebig viele Tische mit je sechs Sitzplätzen
– Eine Karte mit Speisen und Getränken
– Pro Tisch eine(n) KellnerIn, der Einfachheit halber im folgenden Kellnerin genannt
In einer Webanwendung sollen
– solche Tische ausgewählt werden können, an denen mindestens ein Gast sitzt.
– für den ausgewählten Tisch die Sitzplätze ausgewählt werden können, auf denen jemand sitzt.
– Für den ausgewählten Sitzplatz die Speisen angezeigt werden, die der Gast bestellt hat.
– Die Kellnerin soll der Reihe nach alle Gäste an einem nicht-leeren Tisch bedienen (Bestellung aufnehmen/liefern) und, wenn alle durch sind, wieder von vorne beginnen. Es wird z.B. zuerst pro Gast ein Getränk geliefert (falls bestellt), dann pro Gast eine Vorspeise (falls bestellt) usw.
– Die Kellnerin soll manuell gestartet und angehalten werden können, damit man jederzeit in Ruhe den aktuellen Stand der Speisen und Getränke pro Gast studieren kann. Solange die Kellnerin läuft, wird ständig serviert, und die Anzeige des ausgewählten Sitzplatzes (der laufend dort bestellten Produkte) aktualisiert sich automatisch.
Mein Ansatz für die Umsetzung:
DOM:
– Ein Eingabefeld für die Bezeichnung eines neu „eröffneten“ Tisches (ein erster Gast hat daran Platz genommen), z.B. „Tisch1“ eingeben und bestätigen mit Eingabetaste.
– Eine Dropdown-Liste zum Auswählen eines besetzten Tisches (deaktiviert falls keiner besetzt ist)
– Ein Entfernen-Button zum Löschen eines Tisches aus der Liste, z.B. wenn kein Gast mehr am Tisch sitzt oder der Tisch aus anderem Grund uninteressant ist..
– Ein Tisch-Element (z.B. <DIV>) für den ausgewählten Tisch.
– Sechs Sitzplatz-Buttons als Kindelemente des Tisches zum Auswählen eines Platzes (deaktiviert falls unbesetzt)
– Anzeigeelemente für die am gewählten Sitzplatz bestellten Produkte
– Ein Start- und ein Stop-Button für die Kellnerin
In einem objektorientierten Ansatz würde ich folgendes implementieren:
– Einen Ur-Tisch, von dem Instanzen für konkrete Tische gebildet werden können
– Eine Ur-Kellnerin, von der Instanzen für konkrete Tische gebildet werden können
– Ein Speisekarten-Objekt für bestellte Produkte
– ...
Und jetzt? Was wird hier Model, was View, und was Controller?
View ist eigentlich klar: Das sind die DOM-Elemente. Denen würde ich einfach Eventhandler-Methoden verpassen, z.B. für das Tischeingabe-Element in JS:
tischInput.onchange = function(){ createTable(this.value) && fire('tableOpened'); };
und hätte somit einen neu eröffneten Tisch.mit dem eingegebenen Namen. Analog mit den anderen Bedienelementen im DOM. Buttons bekommen u.a. onclick-Methoden, die dann die entsprechende Aktion durchführen usw. Sitzplatz-Buttons z.B. reagieren auf den tableOpened-Event (der seinerseits durch den Change-Event des Eingabefelds für einen Tisch ausgelöst wurde) und werden daraufhin erst sichtbar.
So weit, so gut. Aber fehlt da nicht irgendwie eine konkrete Controller-Schicht? Die Funktionen sind ja jetzt direkt in den DOM-Elementen (View) als Eventhandler verankert, zählen die trotzdem als Controller? Und wo ist überhaupt das 'Model'? Die Kellnerin kann's ja nicht sein, sonst würde sie nicht im Restaurant bedienen ;)
Versteht jemand, was ich meine? Irgendwie habe ich ein Problem, Model, View und Controller konkret auseinanderzuhalten bzw. überhaupt zu bauen. Hat mein Konzept deshalb einen Designfehler? Hat es überhaupt etwas mit dem MVC-Muster zu tun oder kann man MVC für mein Beispiel gar nicht brauchen?
Danke für jede Anregung,
Don P
Hi Nachbar,
interessante Aufgabenstellung!
Mein Ansatz:
Das Objekt O ist der Tisch, (der von <s>Kneipe</s> Restaurant erben könnte)
O hat Eigenschaften wie Anzahl der Sitzplätze und Plätze die schon belegt sind.
O hat Methoden wie z.b. das Bestellen einer Speise. Das Servieren könnte eine Methode von Restaurant sein, die geerbt wird.
Das wäre die Modellierung M
Jetzt kommt der Gast ins Spiel, der wiederum bestimmte Methoden nutzt, um den Tisch, das Objekt seinen Wünschen entsprechend zu gestalten.
Das muss kontrolliert werden, Controler C
Wenn wir das haben, kann die Darstellung erfolgen, V
V kann sein: O konnte erstellt werden.
V kann auch sein: O konnte nicht erstellt werden, weil ein Stuhl fehlt.
Nehmen wir an, O konnte erstellt werden, gehts weiter mit den Aufrufen der Methoden, ein Gast bestellt einen Dinosaurier. Kann fehlschlagen, weil ausgestorben.
Gast nimmt Methode Bestellung und bestellt ein Bier, ok, das geht. Damit wird eine weitere Methode aufgerufen: Bier servieren.
C wacht über die Benutzereingaben, die Gast macht: Von der Objekterstellung über die Aufrufe der Methoden bis zur Zerstörung des Objekts, in cleanup (Destroy) müssen z.B. das Geschirr und die leeren Gläser noch weggeräumt werden.
Über andere Ansätze freue ich mich mit Dir,
Hotti
Hi!
Das Objekt O ist der Tisch, (der von <s>Kneipe</s> Restaurant erben könnte)
Warum das denn? Im Restaurant gibt es eine Küche, eine Toilette und noch einiges anderes mehr. Was hat der Tisch damit am Hut? Tisch ist eine eigene Klasse. Das Restaurant führt höchstens eine Liste der Tische als eine seiner Eigenschaften.
O hat Eigenschaften wie Anzahl der Sitzplätze und Plätze die schon belegt sind.
Die Anzahl muss kein eigenständige Eigenschaft sein. Da reicht ein berechneter Wert, der aus der List der Sitzgelegenheiten gebildet wird. Schließlich kann man ja Stühle vom Nachbartisch hinzufügen. Sitzgelegenheiten sind auch nciht weiter wichtig, schließlich könnte ein Gast auch neben dem Tisch stehend was trinken.
O hat Methoden wie z.b. das Bestellen einer Speise. Das Servieren könnte eine Methode von Restaurant sein, die geerbt wird.
So funktioniert Objektorientierung nicht. Wenn du die Zuständigkeiten nicht ordentlich trennst und modellierst, kannst du es auch mit der OOP lassen. - Eine Kuh gibt zwar Milch, aber Melken ist keine Methode der Kuh. Dazu nimmt man ein anderes Objekt, einen Melker oder eine Melkmaschine. Eine Melkmaschine erbt auch nicht von einem Melker oder umgekehrt. Wohl aber haben beide das Interface implementiert, das eine Methode Melken hat, dem eine Instanz von Kuh übergeben wird.
Jetzt kommt der Gast ins Spiel, der wiederum bestimmte Methoden nutzt, um den Tisch, das Objekt seinen Wünschen entsprechend zu gestalten.
Das muss kontrolliert werden, Controler C
Das Problem an solchem Unsinn ist, dass man gar nicht weiß, wo man bei der Richtigstellung zuerst ansetzen soll. Deswegen lass ich das fachlich unkommentiert stehen.
Nehmen wir an, O konnte erstellt werden, gehts weiter mit den Aufrufen der Methoden, ein Gast bestellt einen Dinosaurier. Kann fehlschlagen, weil ausgestorben.
Das ist ein Problem der Eingabedatenvalidierung, keins das mit MVC zu tun hat.
Gast nimmt Methode Bestellung und bestellt ein Bier, ok, das geht. Damit wird eine weitere Methode aufgerufen: Bier servieren.
Du beschreibst einen Getränkeautomat, kein komplexes Restaurant wie in der Anforderung.
C wacht über die Benutzereingaben, die Gast macht:
Das ist eine Aufgabe für ein Observer-Pattern und nichts für MVC.
Von der Objekterstellung über die Aufrufe der Methoden bis zur Zerstörung des Objekts, in cleanup (Destroy) müssen z.B. das Geschirr und die leeren Gläser noch weggeräumt werden.
Äpfel und Birnen. Das Geschirr soll nicht zerstört werden. Lediglich abgeräumt werden muss es. Die Tassen- und Teller-Instanzen müssen nur von der Collection im Tisch-Objekt zu der der Spülmaschine gebracht werden. Später dann in die vom Schrank.
Ansonsten sind Objekte erzeugen und zerstören Nebenhandlungen beim Programmieren. Das wird nicht alles vom Controller überwacht, dafür gibt es den Garbage Collector. Selbst wenn man einen solchen nicht hat, gibt es Objekte, die außerhalb des Controllers erzeugt werden, mit denen er aber arbeiten muss und die auch nach dessen Arbeitsbeendigung noch existieren (müssen). Das ist ebenfalls nichts, das mit mit dem MVC-Pattern direkt was zu tun hat.
Das Abräumen des Tisches ist eine Aktion, die ein Kellnerin-Controller erledigen kann, aber die Auslösung dieser Aufgabe braucht irgendeinen externen Anstoß - sei es durch den Anwender ("bitte mal abräumen") oder über eine Observierung (aufgegessen / Platz verlassen). Üblicherweise ist die Kellnerin Observator und Controller in Personalunion. Beim Programmieren würde man das jedoch sicher nicht vereinen. Da könnte man beim Nobelrestaurant eine Anleihe nehmen, das einen Oberkellner hat, der lediglich den Gästen die Wünsche von den Augen abliest und Aktionen der spezialisierten Controller (Sommelier, Essenbringer, Abräumer, wasweißich) anstößt.
Lo!
hi,
Das muss kontrolliert werden, Controler C
Das Problem an solchem Unsinn ist, dass man gar nicht weiß, wo man bei der Richtigstellung zuerst ansetzen soll. Deswegen lass ich das fachlich unkommentiert stehen.
Wir schauen noch einmal in die Aufgabenstellung: Es soll eine Webanwendung sein. Wir haben Request/Response und wenn wir mit OOP herangehen ist die Response ein Objekt. Das Objekt braucht eine Methode, damit es ausgegeben werden kann. Und der Request kann Parameter enthalten, also muss das Objekt auch eine Methode haben zum Lesen der Parameter. Wobei: Das Objekt kann die Methode zum Lesen der Parameter entweder erben oder delegieren oder wir schreiben die Methode selbst (gibts alles schon, wäre Zeitverschwendung).
Jetzt kommt der Controler ins Spiel, den können wir Frontcontroler nennen, weil er an vorderster Front die Parameter kontrolliert, bevor überhaupt ein View (Objekt als Response ausgeben) entstehen kann.
Tische, die bestellt werden können, gibt es enweder konfektioniert oder maßgeschneidert. Letzteres heißt: Über Benutzereingaben können bestimmte Eigenschaften des Tisch-Objekts customized werden. Läuft auch über den Kontroller. Jetzt aufgepasst: Wir haben zwei Objekte, den Tisch und das Response-Objekt. Der Frontkontroller muss über beiden stehen!
Wir können jetzt Folgendes machen, was nicht der OOP widerspricht: Machen wir den Tisch zum Attribut des Response-Objekts. D.h., wenn wir die Response ausgeben, ist der Tisch dabei. Und: die Methoden, die den Tisch customizen, sind Methoden des Response-Objekts.
Das Objekt muss _vor_ dem Frontkontroller erstellt werden, weil der FC die Methoden des Objekts braucht zum Entgegennehmen der Benutzereingaben. Erst wenn der FC seine Zustimmung gibt, wird das Objekt vervollständigt und kann ausgegeben werden.
Hotti
Hallo,
Das Objekt O ist der Tisch, (der von <s>Kneipe</s> Restaurant erben könnte)
Tisch erbt von Restaurant? Ein Tisch hat also auch Angestellte? Lieferanten? Und eine Anschrift?
O hat Methoden wie z.b. das Bestellen einer Speise. Das Servieren könnte eine Methode von Restaurant sein, die geerbt wird.
Ein Tisch bestellt eine Speise? Ein Restaurant serviert die Speisen?
Das wäre die Modellierung M
Nein.
Jetzt kommt der Gast ins Spiel, der wiederum bestimmte Methoden nutzt, um den Tisch, das Objekt seinen Wünschen entsprechend zu gestalten.
Blumendekoration?
Wenn wir das haben, kann die Darstellung erfolgen, V
V kann sein: O konnte erstellt werden.
V kann auch sein: O konnte nicht erstellt werden, weil ein Stuhl fehlt.
Schmarn.
ein Gast bestellt einen Dinosaurier. Kann fehlschlagen, weil ausgestorben.
Kann nicht fehlschlagen, da Option Dinosaurier nicht auf dem Menu vorhanden ist.
Gast nimmt Methode Bestellung und bestellt ein Bier, ok, das geht. Damit wird eine weitere Methode aufgerufen: Bier servieren.
Zwischen dem Aufruf von "Bestellen" und "Servieren" liegen etliche Aktionen (ganz zu schweigen von Zustaendigkeiten). Und ganz sicher kann ein Gast nicht die Aktion "Servieren" aufrufen.
C wacht über die Benutzereingaben, die Gast macht:
Dafuer sind nicht die Controller zustaendig.
Von der Objekterstellung über die Aufrufe der Methoden bis zur Zerstörung des Objekts, in cleanup (Destroy) müssen z.B. das Geschirr und die leeren Gläser noch weggeräumt werden.
Auch hierfuer nicht (zwingend).
Über andere Ansätze freue ich mich mit Dir,
Das ist keine Ansatz.
Nichts fuer ungut, aber dein Wissen ist gefaehrlich. Zumindest solltest du Fragende, die sich mit dem MVC-Pattern beschaeftigen, nicht solchen Unsinn erzaehlen (um das noch einmal mehr zu verdeutlichen habe ich trotz dedlfix fachlicher Antwort auch noch meinen Beitrag dazu gegeben). Denn jede Aussage aus deinem Posting ist fachlich falsch. Es herrschen eklatante Missverstaendnisse was sowohl die objektorientierte Programmierung als auch das MVC-Pattern angeht.
Grusz,
Christopher
Hi!
Versteht jemand, was ich meine? Irgendwie habe ich ein Problem, Model, View und Controller konkret auseinanderzuhalten bzw. überhaupt zu bauen. Hat mein Konzept deshalb einen Designfehler? Hat es überhaupt etwas mit dem MVC-Muster zu tun oder kann man MVC für mein Beispiel gar nicht brauchen?
Du beschreibst mehr oder weniger eine ziemlich komplexe Geschäftslogik. Die kannst du nicht komplett in eine einzelne Instanz eines Model-View-Controller-Trios packen. MVC ist ein Muster, wie eine Benutzeraktion/-eingabe durch den Controller gesteuert in Richtung Model eine Aktion mit Daten (speichern, lesen, irgendwohin senden, etwas berechnen) ausführt und das Ergebnis in eine View bringt, die etwas für den Anwender Sichtbares erzeugt.
Die Kellnerin wäre vielleicht eine Art Controller. Sie nimmt die Eingabe entgegen, reicht sie an das Model weiter, was in einem Fall die Küche ist, im anderen die Theke und zum Schluss die Kasse. Views wären dann die Auslieferung des Bestellten und der Rechnung. Dass die Kellnerin bei einer Anforderung erst zu einem Tisch hinlaufen muss, könnte man vernachlässigen. Theoretisch könnten die Gäste ihre Bestellungen auch quer durch den Raum rufen. Allerdings kann man das auch in eine MVC-Aktion bringen: Gast macht sich bemerkbar, Model braucht es dafür keins, die View ist die Präsenz der Kellnerin mit dem Eingabeformular für den Gast - sprich: ihren gespitzen Ohren.
Nun ist es ja so, dass die Aktionen teilweise recht lange dauern. Während des MVC-Zyklus für die Bestellung eines Essens und dessen Auslieferung werden weitere Ereignisse ausgelöst, weil andere Gäste auch Wünsche haben. Schon wenn einer zwei Wünsche hat - Essen und Getränk sind bereits zwei - kann das ein Controller erledigen. Dazu würde ich für jeden Request einen neuen Thread eröffnen. Deine Kellnerin muss für eine ordnungsgemäße Ausführung allerdings thread-safe sein. Die Request-Threads werden jeweils für sich zum jeweiligen Model (Küche, Bar, etc.) geleitet und auf dessen Fertigstellung gewartet, bevor die Ausliefung als View erfolgen kann.
Die Ereignisauslösung ist aber nicht Teil des MVC-Musters. Für die Simulierung der Gäste und der Generierung ihrer Wünsche musst du dir was anderes ausdenken. - Im wahren Leben muss die Kellnerin auch darauf achten, dass die Gäste nicht ohne zu Bezahlen verschwinden. Hierzu könntest du ein Observer-Pattern einbauen. Des ist ebenfalls kein Bestandteil des MVC-Musters. Das Observieren würde ich in einem Programm einer anderen Instanz als der Kellnerin überhelfen. Wenn jedoch der Observer anschlägt, muss das zu einem Request an den Controller führen, der eine View an den Platz schickt, die an die noch notwendige Rechnungswunsch-Aktion erinnert.
Im wirklichen Restaurant-Betrieb ist das Observer-Pattern noch deutlich häufiger in Verwendung. Die Kellnerin eröffnet in ihrem Gedächtnis nicht unbedingt für jeden Bestellungsteil einen Thread. Vielmehr bündelt sie erstmal alles, sortiert es dann für Küche und Bar auseinander und unterbricht diesen Request erst einmal. Mit dem Observer-Pattern in Richtung Küche und Bar wird sie beim Fertigstellen informiert und nimmt dann ihre Arbeit wieder auf. Für eine solche Simulation ist das MVC-Pattern vielleicht nicht mehr das Richtige, weil der typische MVC-Zyklus unterbrochen wird. Aber viele Wege führen nach Rom und man kann durchaus auch das MVC-Pattern nehmen und es nach Belieben umgestalten.
Wie du vielleicht feststellen wirst, bin ich nicht auf alle Einzelheiten deiner Beschreibung eingegangen, denn beispielsweise gleich die ersten
– Beliebig viele Tische mit je sechs Sitzplätzen
– Eine Karte mit Speisen und Getränken
sind für das MVC-Pattern nicht von Belang. Auch hast du eher ein Thema gewählt, das mit einer Echtzeit-Simulation besser realisiert werden kann als mit einer Webanwendung, die ja immer nur jeweils einen Request abarbeitet und dazwischen nichts macht. Du müsstest deine Gäste außerhalb der Anwendung platzieren, denn ansonsten hast du keine Ereignisse, auf die dein Controller reagieren könnte. Das hat auch noch weitere Nachteile, weil die Gäste pro Request ein neues Browser-Fenster öffnen müssen oder zumindest je einen Ajax-Request, denn eine asynchrone Rückmeldung seitens des Servers ist nicht vorgesehen. (Bitte nicht verwechseln mit dem asynchronen Einbau der Response in die Webseite.) Entweder der Gast bekommt sein Essen/Getränk mit der Response zum Request oder er pollt ständig, ob das Zeug fertig ist. Das entspricht jedoch eher einem McDonalds-Restaurant, bei dem vieles schon ferig ist, maches aber noch eine Weile braucht und dessen Fertigstellung an einer Schautafel angekündigt wird. Außerdem müsste irgendwas im Hintergrund zum einen solche Requests abarbeiten und andererseits auf Vorrat Produkte ("Speisen" will ich es lieber nicht nennen) erzeugen, was auch nicht in den üblichen Ausführungen von Webservern implementiert ist.
Lo!
Hallo Don P,
ich würde das über einen Workflow lösen.
Ein paar links:
http://de.wikipedia.org/wiki/Workflow
http://de.wikipedia.org/wiki/Workflow-Management
http://de.wikipedia.org/wiki/Enterprise_Content_Management_System#Manage_.28Verwaltung.2C_Bearbeitung.2C_Nutzung.29
http://www.wfmc.org/
Kay