Was geht in den "Controller" in "MVC"?

186

Ich glaube, ich verstehe die Grundkonzepte von MVC - das Modell enthält die Daten und das Verhalten der Anwendung, die Ansicht ist dafür verantwortlich, sie dem Benutzer anzuzeigen, und der Controller kümmert sich um Benutzereingaben. Ich bin mir nicht sicher, was genau im Controller steckt.

Nehmen wir zum Beispiel an, ich habe eine ziemlich einfache Anwendung (ich denke speziell an Java, aber ich nehme an, dass die gleichen Prinzipien auch anderswo gelten). Ich meinen Code in 3 Pakete genannt organisieren app.model, app.viewund app.controller.

Innerhalb des app.modelPakets habe ich einige Klassen, die das tatsächliche Verhalten der Anwendung widerspiegeln. Diese extends Observableund verwenden setChanged()und notifyObservers()lösen die Ansichten aus, um sie gegebenenfalls zu aktualisieren.

Das app.viewPaket verfügt über eine Klasse (oder mehrere Klassen für verschiedene Anzeigetypen), die javax.swingKomponenten für die Anzeige verwendet. Einige dieser Komponenten müssen in das Modell zurückgeführt werden. Wenn ich das richtig verstehe, sollte die Ansicht nichts mit dem Feedback zu tun haben - das sollte vom Controller behandelt werden.

Was stelle ich eigentlich in den Controller? Füge ich das public void actionPerformed(ActionEvent e)in die Ansicht ein, indem ich nur eine Methode im Controller aufrufe? Wenn ja, sollte eine Validierung usw. im Controller durchgeführt werden? Wenn ja, wie kann ich Fehlermeldungen an die Ansicht zurückmelden - sollte dies das Modell erneut durchlaufen oder sollte der Controller es einfach direkt an die Ansicht zurücksenden?

Was füge ich in den Controller ein, wenn die Validierung in der Ansicht erfolgt?

Entschuldigung für die lange Frage, ich wollte nur mein Verständnis des Prozesses dokumentieren und hoffentlich kann jemand dieses Problem für mich klären!

Paul Walker
quelle

Antworten:

520

In dem von Ihnen vorgeschlagenen Beispiel haben Sie Recht: "Der Benutzer hat auf die Schaltfläche" Dieses Element löschen "geklickt" in der Benutzeroberfläche sollte grundsätzlich nur die "Löschen" -Funktion des Controllers aufrufen. Der Controller hat jedoch keine Ahnung, wie die Ansicht aussieht, und daher muss Ihre Ansicht einige Informationen wie "Welches Element wurde angeklickt?" Erfassen.

In einem Gesprächsformular:

Ansicht : "Hey, Controller, der Benutzer hat mir gerade gesagt, dass er möchte, dass Punkt 4 gelöscht wird."
Controller : "Hmm, nachdem er seine Anmeldeinformationen überprüft hat, darf er das tun ... Hey, Model, ich möchte, dass Sie Punkt 4 erhalten und alles tun, um ihn zu löschen."
Modell : "Punkt 4 ... hat es. Es wurde gelöscht. Zurück zu Ihnen, Controller."
Controller : "Hier werde ich den neuen Datensatz sammeln. Zurück zu Ihnen, Ansicht."
Ansicht : "Cool, ich zeige dem Benutzer das neue Set jetzt."

Am Ende dieses Abschnitts haben Sie eine Option: Entweder kann die Ansicht eine separate Anforderung stellen, "Geben Sie mir den neuesten Datensatz" und damit reiner sein, oder der Controller gibt den neuen Datensatz implizit mit "Löschen" zurück " Operation.

Andres Jaan Tack
quelle
90
Dieser Dialog ist die beste Erklärung für MVC, die mir begegnet ist, danke!
Paul Walker
13
Alle in Ordnung, aber es ist nichts falsch mit dem Blick zu lesen direkt aus dem Modell. "Controller sind nicht die Datenpolizei". Es gibt auch eine Doktrin, die besagt, dass Controller dünn gehalten werden sollen. Ansichtshelfer sind der perfekte Ort, um Daten zu sammeln, die von Ihrer Ansicht verwendet werden können. Man sollte nicht den gesamten Controller-Stack weiterleiten müssen, um eine Datenzugriffslogik wiederzuverwenden. Weitere Details: rmauger.co.uk/2009/03/…
Ausnahme e
1
Ich stimme "Ausnahme e" zu. Die Daten im Modell können durch viele Ereignisse aktualisiert werden, nicht unbedingt durch die Steuerung, und daher signalisiert das M in einigen MVC-Designs dem V, dass die Daten verschmutzt sind, und das V kann sich selbst aktualisieren. Das C spielt in diesem Fall keine Rolle.
Mishax
68

Das Problem dabei MVCist, dass die Leute denken, dass die Ansicht, der Controller und das Modell so unabhängig wie möglich voneinander sein müssen. Sie denken nicht - eine Ansicht und ein Controller sind oft miteinander verflochten - als M(VC).

Der Controller ist der Eingabemechanismus der Benutzeroberfläche, der insbesondere bei GUIs häufig in der Ansicht verwickelt ist. Trotzdem wird die Ansicht ausgegeben und der Controller eingegeben. Eine Ansicht kann oft ohne einen entsprechenden Controller funktionieren, aber ein Controller ist ohne Ansicht normalerweise weitaus weniger nützlich. Benutzerfreundliche Controller verwenden die Ansicht, um die Benutzereingaben aussagekräftiger und intuitiver zu interpretieren. Dies macht es schwierig, das Controller-Konzept von der Ansicht zu trennen.

Stellen Sie sich einen funkgesteuerten Roboter auf einem Erkennungsfeld in einer versiegelten Box als Modell vor.

Das Modell dreht sich alles um Zustand und Zustandsübergänge ohne Konzept der Ausgabe (Anzeige) oder was die Zustandsübergänge auslöst. Ich kann die Position des Roboters auf dem Feld ermitteln und der Roboter weiß, wie er die Position wechselt (einen Schritt vorwärts / rückwärts / links / rechts). Ohne Ansicht oder Steuerung leicht vorstellbar, aber nichts Nützliches

Stellen Sie sich eine Ansicht ohne Controller vor, z. B. jemanden in einem anderen Raum im Netzwerk in einem anderen Raum, der die Position des Roboters als (x, y) -Koordinaten beobachtet, die über eine Bildlaufkonsole gestreamt werden. Diese Ansicht zeigt nur den Status des Modells an, aber dieser Typ hat keinen Controller. Auch diese Ansicht ist ohne Controller leicht vorstellbar.

Stellen Sie sich eine Steuerung ohne Sicht vor, z. B. jemanden, der in einem Schrank eingeschlossen ist und dessen Funksteuerung auf die Frequenz des Roboters abgestimmt ist. Dieser Controller sendet Eingaben und verursacht Zustandsübergänge, ohne zu wissen, was sie mit dem Modell tun (wenn überhaupt). Leicht vorstellbar, aber ohne Feedback aus der Ansicht nicht wirklich nützlich.

Die meisten benutzerfreundlichen Benutzeroberflächen koordinieren die Ansicht mit dem Controller, um eine intuitivere Benutzeroberfläche bereitzustellen. Stellen Sie sich beispielsweise eine Ansicht / Steuerung mit einem Touchscreen vor, der die aktuelle Position des Roboters in 2D anzeigt und es dem Benutzer ermöglicht, den Punkt auf dem Bildschirm zu berühren, der sich gerade vor dem Roboter befindet. Der Controller benötigt Details zur Ansicht, z. B. die Position und den Maßstab des Ansichtsfensters und die Pixelposition des berührten Punkts relativ zur Pixelposition des Roboters auf dem Bildschirm, um dies richtig zu interpretieren (im Gegensatz zu dem Mann, der mit im Schrank eingeschlossen ist) die Funksteuerung).

Habe ich deine Frage schon beantwortet? :-)

Der Controller ist alles, was Eingaben vom Benutzer entgegennimmt, mit denen das Modell in den Übergangszustand versetzt wird. Versuchen Sie, die Ansicht und den Controller getrennt zu halten, stellen Sie jedoch fest, dass sie häufig voneinander abhängig sind. Daher ist es in Ordnung, wenn die Grenze zwischen ihnen verschwommen ist, dh wenn Ansicht und Controller als separate Pakete nicht so sauber voneinander getrennt sind wie Sie wie, aber das ist okay. Möglicherweise müssen Sie akzeptieren, dass der Controller nicht sauber von der Ansicht getrennt wird, da die Ansicht vom Modell stammt.

... sollte eine Validierung usw. im Controller durchgeführt werden? Wenn ja, wie kann ich Fehlermeldungen an die Ansicht zurückmelden - sollte dies das Modell erneut durchlaufen oder sollte der Controller es einfach direkt an die Ansicht zurücksenden?

Was füge ich in den Controller ein, wenn die Validierung in der Ansicht erfolgt?

Ich sage, eine verknüpfte Ansicht und ein Controller sollten frei interagieren, ohne das Modell durchzugehen. Der Controller nimmt die Benutzereingaben entgegen und sollte die Validierung durchführen (möglicherweise unter Verwendung von Informationen aus dem Modell und / oder der Ansicht). Wenn die Validierung jedoch fehlschlägt, sollte der Controller in der Lage sein, die zugehörige Ansicht direkt zu aktualisieren (z. B. Fehlermeldung).

Der Härtetest besteht darin, sich zu fragen, ob eine unabhängige Ansicht (dh der Mann im anderen Raum, der die Roboterposition über das Netzwerk beobachtet) aufgrund eines Validierungsfehlers eines anderen (z. B. des Mannes im Schrank) etwas sehen sollte oder nicht versuchte den Roboter anzuweisen, das Feld zu verlassen). Im Allgemeinen lautet die Antwort nein - der Validierungsfehler hat den Zustandsübergang verhindert. Wenn es keine staatliche Transformation gab (der Roboter bewegte sich nicht), müssen die anderen Ansichten nicht mitgeteilt werden. Der Typ im Schrank hat einfach keine Rückmeldung erhalten, dass er versucht hat, einen illegalen Übergang zu verursachen (keine Ansicht - schlechte Benutzeroberfläche), und niemand sonst muss das wissen.

Wenn der Typ mit dem Touchscreen versuchte, den Roboter vom Feld zu schicken, erhielt er eine nette benutzerfreundliche Nachricht, in der er darum gebeten wurde, den Roboter nicht zu töten, indem er ihn vom Erkennungsfeld schickte, aber auch dies muss niemand anderes wissen.

Wenn andere Ansichten über diese Fehler Bescheid wissen müssen, sagen Sie effektiv, dass die Eingaben des Benutzers und alle daraus resultierenden Fehler Teil des Modells sind und das Ganze etwas komplizierter ist ...

Bert F.
quelle
23

Hier ist ein guter Artikel über die Grundlagen von MVC.

Es sagt aus ...

Controller - Der Controller übersetzt Interaktionen mit der Ansicht in Aktionen, die vom Modell ausgeführt werden sollen.

Mit anderen Worten, Ihre Geschäftslogik. Der Controller reagiert auf Aktionen des Benutzers, die in der Ansicht ausgeführt wurden, und reagiert. Sie geben hier die Validierung ein und wählen die entsprechende Ansicht aus, wenn die Validierung fehlschlägt oder erfolgreich ist (Fehlerseite, Meldungsfeld, was auch immer).

Es gibt einen weiteren guten Artikel bei Fowler .

JP Alioto
quelle
MVP ist eine weitere Option, die in dem Artikel behandelt wird, auf den Sie verweisen. Siehe martinfowler.com/eaaDev/ModelViewPresenter.html
Jon
Vielen Dank für die Links, sie sorgen sicherlich für interessante Lektüre.
Paul Walker
18

Das MVC-Muster möchte lediglich, dass Sie die Präsentation (= Ansicht) von der Geschäftslogik (= Modell) trennen . Der Controller-Teil ist nur dazu da, Verwirrung zu stiften.

Dimitri C.
quelle
1
Genau das, was ich bis jetzt immer gedacht habe, aber nie den Mut hatte, es jemandem zu erzählen ... oder vielleicht nicht die richtigen Worte finden konnte.
user1451111
1
Model-View-Confusion
Regen
10

In der Praxis habe ich das Controller-Konzept nie als besonders nützlich empfunden. Ich verwende in meinem Code eine strikte Modell- / Ansichtstrennung, aber es gibt keinen klar definierten Controller. Es scheint eine unnötige Abstraktion zu sein.

Persönlich scheint eine ausgewachsene MVC das werkseitige Designmuster zu sein, da sie leicht zu verwirrendem und überkompliziertem Design führt. Sei kein Architekturastronaut .

John Kugelman
quelle
9

Aufgrund Ihrer Frage habe ich den Eindruck, dass Sie in Bezug auf die Rolle des Modells etwas verschwommen sind. Das Modell ist auf die mit der Anwendung verknüpften Daten fixiert. Wenn die App über eine Datenbank verfügt, besteht die Aufgabe des Modells darin, mit ihr zu sprechen. Es wird auch jede einfache Logik verarbeiten, die diesen Daten zugeordnet ist. wenn Sie eine Regel haben, die besagt, dass für alle Fälle, in denen TABLE.foo == "Hurra!" und TABLE.bar == "Huzzah!" Setzen Sie dann TABLE.field = "W00t!", und Sie möchten, dass sich das Modell darum kümmert.

Der Controller sollte den Großteil des Verhaltens der Anwendung verarbeiten. Um Ihre Fragen zu beantworten:

Platziere ich die öffentliche void actionPerformed (ActionEvent e) in der Ansicht mit nur einem Aufruf einer Methode im Controller?

Ich würde nein sagen. Ich würde sagen, das sollte im Controller leben; Die Ansicht sollte einfach die von der Benutzeroberfläche kommenden Daten in den Controller einspeisen und den Controller entscheiden lassen, welche Methoden als Antwort aufgerufen werden sollen.

Wenn ja, sollte eine Validierung usw. im Controller durchgeführt werden?

Der Großteil Ihrer Validierung sollte wirklich vom Controller durchgeführt werden. Es sollte die Frage beantworten, ob die Daten gültig sind oder nicht. Wenn dies nicht der Fall ist, geben Sie die entsprechenden Fehlermeldungen an die Ansicht weiter. In der Praxis können Sie einige einfache Überprüfungen der Integrität in die Ansichtsebene integrieren, um die Benutzererfahrung zu verbessern. (Ich denke hauptsächlich an Webumgebungen, in denen möglicherweise eine Fehlermeldung angezeigt wird, sobald der Benutzer auf "Senden" klickt, anstatt auf den gesamten Zyklus "Senden -> Verarbeiten -> Seiten laden" zu warten, bevor er mitteilt, dass er Fehler gemacht hat .) Sei einfach vorsichtig; Sie möchten den Aufwand nicht mehr als nötig duplizieren, und in vielen Umgebungen (ich denke wieder an das Web) müssen Sie Daten, die von der Benutzeroberfläche stammen, häufig als schmutziges, schmutziges Paket behandeln lügt bis du '

Wenn ja, wie kann ich Fehlermeldungen an die Ansicht zurückmelden - sollte dies das Modell erneut durchlaufen oder sollte der Controller es einfach direkt an die Ansicht zurücksenden?

Sie sollten ein Protokoll eingerichtet haben, bei dem die Ansicht nicht unbedingt weiß, was als nächstes passiert, bis der Controller dies mitteilt. Welchen Bildschirm zeigen Sie ihnen, nachdem der Benutzer diese Schaltfläche gedrückt hat? Die Ansicht weiß es möglicherweise nicht, und der Controller weiß es möglicherweise auch nicht, bis er die gerade erhaltenen Daten betrachtet. Dies kann "Wie erwartet zu diesem anderen Bildschirm wechseln" oder "Auf diesem Bildschirm bleiben und diese Fehlermeldung anzeigen" sein.

Nach meiner Erfahrung sollte die direkte Kommunikation zwischen dem Modell und der Ansicht sehr, sehr eingeschränkt sein und die Ansicht sollte keine Daten des Modells direkt ändern. Das sollte die Aufgabe des Controllers sein.

Was füge ich in den Controller ein, wenn die Validierung in der Ansicht erfolgt?

Siehe oben; Die eigentliche Validierung sollte im Controller erfolgen. Und hoffentlich haben Sie eine Vorstellung davon, was jetzt in den Controller eingefügt werden soll. :-)

Es ist erwähnenswert, dass an den Rändern alles etwas verschwommen werden kann. Wie bei fast allem, was so komplex ist wie Software-Engineering, wird es eine Fülle von Urteilsforderungen geben. Verwenden Sie einfach Ihr bestes Urteilsvermögen, versuchen Sie, innerhalb dieser App konsistent zu bleiben, und versuchen Sie, die gewonnenen Erkenntnisse auf das nächste Projekt anzuwenden.

BlairHippo
quelle
7

Controller ist wirklich Teil der Ansicht. Seine Aufgabe besteht darin, herauszufinden, welche Dienste zur Erfüllung der Anforderung benötigt werden, Werte aus der Ansicht in Objekte zu entfernen, die für die Dienstschnittstelle erforderlich sind, die nächste Ansicht zu bestimmen und die Antwort wieder in eine Form zu bringen, die die nächste Ansicht verwenden kann . Es behandelt auch alle Ausnahmen, die ausgelöst werden, und rendert sie in Ansichten, die Benutzer verstehen können.

Die Service-Schicht kennt die Anwendungsfälle, Arbeitseinheiten und Modellobjekte. Der Controller ist für jeden Ansichtstyp unterschiedlich. Sie verfügen nicht über denselben Controller für Desktop-, browserbasierte, Flex- oder mobile Benutzeroberflächen. Ich sage also, es ist wirklich Teil der Benutzeroberfläche.

Serviceorientiert: Hier wird gearbeitet.

Duffymo
quelle
3

Die Steuerung dient in erster Linie der Koordination zwischen Ansicht und Modell.

Leider wird es manchmal mit der Ansicht vermischt - in kleinen Apps, obwohl dies nicht so schlimm ist.

Ich schlage vor, Sie setzen die:

public void actionPerformed(ActionEvent e)

in der Steuerung. Dann sollte Ihr Aktionslistener in Ihrer Ansicht an den Controller delegieren.

Was den Validierungsteil betrifft, können Sie ihn in die Ansicht oder in den Controller einfügen. Ich persönlich denke, er gehört in den Controller.

Ich würde definitiv empfehlen, einen Blick auf Passive View und Supervising Presenter zu werfen (in das Model View Presenter im Wesentlichen unterteilt ist - zumindest von Fowler). Sehen:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

Jon
quelle
3

Hier ist eine Faustregel, die ich verwende: Wenn es sich um eine Prozedur handelt, die ich speziell für eine Aktion auf dieser Seite verwenden werde, gehört sie zum Controller und nicht zum Modell. Das Modell sollte nur eine kohärente Abstraktion für den Datenspeicher bereitstellen.

Ich habe mir das ausgedacht, nachdem ich mit einer großen Webanwendung gearbeitet habe, die von Entwicklern geschrieben wurde, die dachten, sie würden MVC verstehen, aber wirklich nicht. Ihre "Controller" sind auf acht Zeilen reduziert, in denen statische Klassenmethoden aufgerufen werden, die normalerweise nirgendwo anders genannt werden: - / machen ihre Modelle kaum mehr als Möglichkeiten zum Erstellen von Namespaces. Wenn Sie dies richtig umgestalten, werden drei Dinge ausgeführt: Verschiebt das gesamte SQL in die Datenzugriffsschicht (auch bekannt als Modell), macht den Controller-Code etwas ausführlicher, aber viel verständlicher und reduziert die alten "Modell" -Dateien auf nichts. :-)

staticsan
quelle
1

Beachten Sie auch, dass jedes Swing-Widget die drei MVC-Komponenten enthält: Jedes verfügt über ein Modell (dh ButtonModel), eine Ansicht (BasicButtonUI) und ein Steuerelement (JButton selbst).

akf
quelle
1

Sie haben im Wesentlichen Recht mit dem, was Sie in den Controller eingeben. Nur so sollte das Modell mit der Ansicht interagieren. Die ausgeführte Aktion kann in der Ansicht platziert werden, die eigentliche Funktionalität kann jedoch in einer anderen Klasse platziert werden, die als Controller fungieren würde. Wenn Sie dies tun, empfehle ich, das Befehlsmuster zu untersuchen, mit dem alle Befehle mit demselben Empfänger abstrahiert werden können. Entschuldigung für den Exkurs.

Auf jeden Fall hat eine ordnungsgemäße MVC-Implementierung nur die folgenden Interaktionen: Modell -> Ansichtsansicht -> Controller-Controller -> Ansicht

Der einzige Ort, an dem es zu einer anderen Interaktion kommen kann, ist, wenn Sie einen Beobachter zum Aktualisieren der Ansicht verwenden, muss die Ansicht den Controller nach den benötigten Informationen fragen.

mnuzzo
quelle
0

Soweit ich weiß, übersetzt der Controller von Aktionen auf der Benutzeroberfläche in Aktionen auf Anwendungsebene. In einem Videospiel übersetzt der Controller beispielsweise "hat die Maus so viele Pixel bewegt" in "möchte in die eine oder andere Richtung schauen". In einer CRUD-App kann die Übersetzung "auf die eine oder andere Schaltfläche geklickt" werden "print this thing", aber das Konzept ist das gleiche.

David Seiler
quelle
0

Wir tun dies auf diese Weise, indem wir Controller hauptsächlich verwenden, um benutzergesteuerte Eingaben / Aktionen zu handhaben und darauf zu reagieren (und _Logic für alles andere, außer Ansicht, Daten und offensichtlichen _Model-Dingen):

(1) (Antwort, Reaktion - was die Webanwendung als Antwort auf den Benutzer "tut") Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> verifyUser_HasProperAuth ()

(2) ("Geschäfts" -Logik, was und wie die Webanwendung "denkt") Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (Ansichten, Portale, wie die Webanwendung "erscheint") Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (nur Datenobjekt, erfasst in _ construct () jedes Blogs * -Klasse erfasst wurde und verwendet wird, um alle Webanwendungs- / Speicherdaten als ein Objekt zusammenzuhalten) Blog_Meta

(5) (Basisdatenschicht, liest / schreibt in DBs) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

Manchmal sind wir ein wenig verwirrt darüber, wo eine Methode im C oder im L platziert werden soll. Aber das Modell ist absolut solide, kristallklar, und da sich alle speicherinternen Daten im _Meta befinden, ist es auch dort ein Kinderspiel . Unser größter Sprung nach vorne war übrigens die Verwendung des _Meta-Einsatzes, da dies den gesamten Rohstoff aus den verschiedenen _C-, _L- und _Model-Objekten entfernte, alles mental einfach zu verwalten machte und uns auf einen Schlag das gab, was ist genannt "Abhängigkeitsinjektion" oder eine Möglichkeit, eine gesamte Umgebung zusammen mit allen Daten zu übertragen (deren Bonus die einfache Erstellung einer "Test" -Umgebung ist).

FYA
quelle