MVC - Wie viel darf mein Controller machen ?

Lexi

Bekanntes Mitglied
Ich bin gerade dabei ein kleines TowerDefense Spiel zu planen und habe mir vorgenommen das ganze nach dem MVC Prinzip zu gestalten. Jetzt stelle ich mir gerade die Frage, was mein Controller eigentlich alles machen darf, bzw was genau seine Aufgabe ist.

Angenommen der User möchte einen neuen Turm bauen und teilt dies dem View per Mausklick mit. Mein View soll jetzt dieses Event erhalten und auf dem Controller
Code:
buildTower()
aufrufen. Bei der Implementation dieser Methode habe ich bisher 2 Varianten :

1. Der Controller nimmt alle nötigen Untersuchungen und Vorbereitungen ( kann ich hier bauen ?, wird die Umgebung des Turms verändert ?, wie viel Geld kostet der Turm ? ...) vor um den Turm an der gewünschten stelle zu platzieren und ruft dann auf dem Model lediglich
Code:
addTower()
auf.

2. Der Controller delegiert die Methode
Code:
buildTower()
einfach an das Model, welches die eigentliche Implementierung( s.o. ) dieser Methode enthält.

Welche Lösung, wenn denn überhaupt eine, ist eurer Meinung nach besser ?

Ich persönlich würde die 1 Variante bevorzugen, da ich die Aufgabe des Model vorrangig in der Speicherung der Daten gesehen habe, deren einzige Manipulation über get - und setMethoden ermöglicht wird.

lg Lexi

PS: Wie kann ich hier im Forum einzeilige Code-Tags verwenden, sodass ich kleine Schnipsel im Fließtext ohne Absatz integrieren kann ?
 
Zuletzt bearbeitet:

javimka

Top Contributor
So wie du es beschrieben hast, hört es sich an, als hätte die View eine Referenz auf den Controller. Das ist imho nicht korrekt. Model, View und Controller sollten soviel ich weiss, folgendermassen instanziert werden:
Java:
Model model = new Model();
View view = new View(model);
Controller controller = new Controller(model, view);
Ich denke, es ginge auch in Ordnung, wenn der Kontroller selber das Model und die View erstellt, aber das View sollte keine Referenz auf den Kontroller haben. Der Kontroller müsste am View dann Listener installieren, er könnte z.B. MouseListener implementieren und sich selbst als MouseListener an die View hängen. Wird dann mousePressed aufgerufen, würde ich dort eine Kontroller-Methode buildTower aufrufen (wie du es auch gemacht hast), die dann entscheided, ob ein Turm gebaut werden kann und gegebenfalls dann model.addTower aufruft.

Ich muss allerdings auch sagen, dass mir die Einhaltung des MVC Patterns auch jedesmal Schwierigkeiten bereitet, besonders, wenn viele verschiedene Panels, also Views zusammen harmonieren müssen oder viele auch kleine Objekte eine Referenz auf das Model haben müssten, was dann irgendwann hässlich wird, wenn man absolut jedem Konstruktor von Objekten das Model übergeben muss.
 

Lexi

Bekanntes Mitglied
War ein bisschen unglücklich ausgedrückt mit der Referenz der View auf den Controller, sollte eigentlich garnicht so sein.

Deiner Meinung nach ist das Model also nur für das simple halten der Daten zuständig ? So wie das bei mit im ersten Beispiel beschrieben ist ?
 

javimka

Top Contributor
Ja, es beinhaltet den Status des Programms. Die View ist da, um diese Daten irgendwie zu visualisieren und der Kontroller verarbeitet die Befehle des Benutzers und verändert das Model entsprechend.
 

KSG9|sebastian

Top Contributor
MVC ist halt auch ein Stück auslegungssache.

Das Model enthält nicht nur blind die Daten, es stellt auch eine Zugriffsschicht auf die Daten bereit.

Wie die Referenzen abgebildet werden kommt auch ein Stück weit auf die Implementierung an.

Ganz wichtig sind 2 Dinge:

- Das Model darf keine Referenz auf die View haben
- Das Model darf keine Referenz auf den Controller haben.

View und Model interagieren über Listener, z.B: in der View: model.addChangeListener(this).

Meist treten View und Controller als Paar auf, das Model gibt es jedoch nur einmal.
Der Controller meldet sich (meist abstrahiert per Interface) an der View an, z.B. view.setInteractionController(this).

So, nun zur Frage was die View darf. Hier gibt es mehrere Möglichkeiten.
Laut Lehrbuch darf die View auch das Model nach Daten fragen. Somit muss die interaktion nicht unbedingt über den Controller gehen.
Allerdings gibt es meist keine Interaktion zwischen Model<>View beim hinzufügen, da sich darauf schlecht "hören" läst.

Die View behandelt meist Änderungen an sämtlichen Modelobjekten oder an den aktuell angezeigten. Operationen wie add, delete, update laufen meist über den Controller.

Hier mal ein kleines Codebeispiel:

Code:
public class Model{
   public void addModelChangeListener(ModelChangeListener l){
      ...
   }
   addCustomer(..){..}
   getCustomer(String id){..}
   removeCustomer(Customer customer){..}
}


public class Controller implements InteractionController{
   
    public Controller(){
        view.addInteractionController(this);
    }
    public void handeAddCustomer(Customer cust){
        model.addCustomer(cust);
    }
}

public class View{
  
     public View(Model model){
           model.addModelChangeListener(this);
     }
     public void handleCustomerChange(Customer cust){
          if(cust == selectedCustomer) { updateSelected(cust); }
     }
  
     public void setInteractionController(InteractionController c){..}
 
     public void buildGui(){
          JButton addCust = new JButton();
          addCUst.addActionListener(){
                 public void actionPerformed(ActionEvent evt){
                        listener.handleAddCustomer(new Customer(txtId.getText(), txtName.getText());
                 }   
           };
     }
}

Es gibt immer Spielräume wie was implementiert werden kann.
Dieses Pattern wie ich es oben teilweise dargestellt habe wird für größere Anwendungen schnell unübersichtlich.
Meist Teile ich das ganze folgendermaßen auf:

- jeder "Geschäftsbereich" ist ein eigenes Modul
- jedes Modul registriert sich an der MainApplication
- die MainApplication kommuniziert mit den Modulen über Events
- die Module kommunizieren über die MainApplication ebenfalls über Events

Das ganze funktioniet über eine Art "EventDispatcher"-Pattern.
Die Main-App ist nach dem MVC-Pattern gebaut, jedes Modul folgt ebenfalls dem MVC-Pattern.


Für die Verteilung der Aufgaben würde ich es folgendermaßen machen:

- View baut aus den Daten das ModelObjekt (den Tower)
- der Controller validiert die fachlichen Inhalte des Towers
- das Model validiert die technischen Inhalte (Datenkonsistenz, prüfen ob das Objekt schon vorhanden ist, optimistic lock u.s.w.)

Allerdings heiß es nicht dass diese Variante richtig ist und alle anderen falsch. Es kommt auch ein Stück weit auf den Aufbau der Anwendung und der verwendeten Frameworks an.

Gruß Sebastian
 

KSG9|sebastian

Top Contributor
So wie du es beschrieben hast, hört es sich an, als hätte die View eine Referenz auf den Controller. Das ist imho nicht korrekt. Model, View und Controller sollten soviel ich weiss, folgendermassen instanziert werden:
Java:
Model model = new Model();
View view = new View(model);
Controller controller = new Controller(model, view);
Ich denke, es ginge auch in Ordnung, wenn der Kontroller selber das Model und die View erstellt, aber das View sollte keine Referenz auf den Kontroller haben. Der Kontroller müsste am View dann Listener installieren, er könnte z.B. MouseListener implementieren und sich selbst als MouseListener an die View hängen. Wird dann mousePressed aufgerufen, würde ich dort eine Kontroller-Methode buildTower aufrufen (wie du es auch gemacht hast), die dann entscheided, ob ein Turm gebaut werden kann und gegebenfalls dann model.addTower aufruft.

Ich muss allerdings auch sagen, dass mir die Einhaltung des MVC Patterns auch jedesmal Schwierigkeiten bereitet, besonders, wenn viele verschiedene Panels, also Views zusammen harmonieren müssen oder viele auch kleine Objekte eine Referenz auf das Model haben müssten, was dann irgendwann hässlich wird, wenn man absolut jedem Konstruktor von Objekten das Model übergeben muss.

Ich würde den Controller soweit abstrahieren dass er keine Kenntniss über Mouse/Actionevents o.ä. hat. Wenn der Controller das nämlich weiß muss er sich auch zwingend die Daten der View besorgen und somit den Aufbau und die (Eingabe)felder kennen.
Ich würde Action/Mouselistener u.s.w. in der View implementieren.
In der Implementierung der Listener wird dann der Controller gerufen.

So würde ich es machen, heißt nicht dass deine Lösung das MVC-Pattern verletzt.

Zum Thema mehrere Panels und schwierig einzuhalten würde ich, wie im vorherigen Post geschrieben, ein zweistufiges Konzept verfolgen.

Die Hauptanwendung kümmert sich um das Grundgerüst nach dem MVC-Pattern. Module werden in die Anwendung eingeklingt über Listener/Events. So kann man ganz gut aufsplitten.


** edit **
Zu der Interaktion Model/View/Controller folgendes von mir:

- jeden Teil (Model, View, Controller) würde ich über Interface abstrahieren
- Model implements IModel: addModelChangeListener, addCustomer, deleteCustomer u.s.w.
- View implements IView: displayCustomer(Customer cust), displayAllCustomers(List<Customer> customers) u.s.w.)
- Controller implements InteractionController: handleAddCustomer, handleDeleteCustomer u.s.w.

Jetzt kommt natürlich das Problem mit der View. Baut man nun eine View mit zig Methoden displayA, displayB, displayC oder abstrahiert man das ganze. Ich baue ein "Modul" (Controller/View-Paar) immer pro "Use-Case": EditCustomerModule, ShowCustomerListModule u.s.w. So ist jede View für genau einen Usecas zuständig und muss sich nicht um das wechseln der Panels kümmern.
Die darüberliegende Anwendung kümmert sich um den Wechsel übergeordneter Module über events (getDispatcher().dispatch(new AppEvent(Events.EDIT_CUSTOMER, customer)).
 
Zuletzt bearbeitet:

KSG9|sebastian

Top Contributor
Aber dazu bräuchte man ja dann eine Referenz auf den Controller.

Jein...keine Referenz auf den Controller sondern auf dessen Interface.

Code:
class VIew{
    public View(Model model, InteractionListener listener){
          ..
    }
}


class Controller implements InteractionListener{
     
    .. new VIew(model, this);

}

Die View sollte nicht unbedingt die genaue Implementierung des Controllers kennen - lediglich eine abstraktion der Funktionen welche für die View nötig sind - nämlich die Methoden für die Behandlung der Events.


* edit *
So wird es auch von Sun beschrieben und in den Beispielen gemacht...allerdings wird da der Controller meistens nicht abstrahiert, was mir persönlich nicht sehr gefällt.
 

Marco13

Top Contributor
Ich hatte neulich mal ein TicTacToe nach MVC angefangen - dann aber gesehen, dass es in der FAQ schon ein Beispiel zu MVC gibt. Werde es ggf. trotzdem noch posten - ist etwas ausführlicher, und könnte dann vielleicht für ein Adventskalendertürchen reichen :D

Ansonsten ist die Titelfrage dieses Threads ja meine Liebilingsfrage zu MVC. Oder wie es durch die ausschweifend-philosophischen Diskussionen und die Einstiegsfrage auf Whatsa Controller Anyway schön beschrieben ist:
What's a Controller, Anyway?
An accountant who gets promoted?


Mal ganz beiläufig: Wie sollte denn (selbst wenn man sich anstrengt, es (selbst mit einem Hack) so falsch zu machen) die View überhaupt das Modell verändern?
 

Marco13

Top Contributor
Aber wo und wann? Eigentlich doch NUR über einen (ggf. anonymen) Listener - und man könnte ja sagen, dass der nicht zur View gehört, sondern ein Controller(chen) ist...
 

javimka

Top Contributor
Wenn du Listener als Controllerchen bezeichnest, hast du doch wieder das Problem, dass Controller und View unabhängig voneinander sein sollten. Um das zu realisieren müsste der Kontroller selber die Listener implementieren und sich dem View anhängen, was die oben diskutierten Probleme verursacht.

Irgendwie werde ich das Gefühl nicht los, dass das MVC-Pattern einfach nicht ganz so perfekt ist, wie es viele, darunter Sun, gerne hätte. Egal wie man es dreht und wendet, hat man wieder irgendwelche Probleme mit der Auslegung des Patterns. Swing selbst ist ja nicht mal selber eine saubere MVC Implementierung. Was ist z.B. ein JPanel? Wohl keine View, denn dazu gibts ja die PanelUI. Ein Model? Aber dann dürfte es die View nicht kennen. Ein Kontroller? Wo ist denn das zugehörige Model.
 

Marco13

Top Contributor
Ja, man kann da viel Philosphieren und auslegen, und oft wird gar nicht der Anspruch erhoben, "DAS" MVC zu implementieren - das reicht dann von MC, MV und MVA bis MVCVC oder so :autsch: "DAS" MVC stammt ... öhm ... aus den 70ern oder so? Jedenfalls ändern sich die Zeiten, aber die (wichtigste) Grundidee (Modell und View zu trennen) ist wohl erhalten geblieben. Trotzdem taucht das MVC auf verschiednenen Ebenen auf: Eine JTable ist eine View für "Eigene" Daten - dass sie selbst wieder ein Model hat, "sieht" man nicht direkt. Und dass selbst das, "was auf dem Bildschirm gerendert wird", eigentlich ein "Modell des TableUI" ist, ist noch eine Ebene...
 

-MacNuke-

Bekanntes Mitglied
Also ich würde da gar nicht so wirklich steif rangehen.

Wichtig ist imo:
- kein doppelter Code
- keine doppelten Models/Daten
- View flexibel

Der Rest ist egal.

D.h. z.B. Swing ist leider alles andere als "MVC" freundlich, wenn auch gerne behauptet.

Nehmen wir mal das Beispiel JTable und JTextfield. Man hat nun z.B. einen Wert aus der Datenbank. Überlichweise zeigt man in einem JTextfield ja gerne Werte an, die dem ausgewählten Datensatz in der JTable entsprechen.

Ja was macht man nun? Für das JTable muss man ein Model implementieren und das JTextfield braucht auch eines. Beide greifen komplett unterschiedlich darauf zu und im View hat das JTable seine TableCellRenderer (ein JLabel) und TableCellEditoren (ein JTextField), aber das JTextfield hat davon nichts. Das heißt man schreibt sich eine Wolf an "Modelverbindern" und Viewformatierungen.

Hier noch das MVC-Prinzip komplett einzuhalten ist schlicht Selbstmord, da man vom Model in ein "View-Model" muss, der dann etwas vom View übernehmen muss, da sonst die Formatierung nicht stimmt.

Und so zum Thema. Passe bei der Programmierung einfach auf, dass du in deinem "Model" nichts hast, was dich irgendwo an deine View bindet. Also haben Swing-Events und -Objekte usw. dort einfach nichts verloren. Schreibe dann ein "Zwischenobjekt" (man könnte es Controller nennen), von welchem du bestimmte Ereignisse aufrufst, "Baue Turm an der Stelle". Dieser Controller sollte aber auch nichts enthalten was dich an diesen bindet. Dieses Objekt könnte auch das Model direkt sein.

Die View übernimmt dann die "Übersetzung, z.B. Swing-Pixel in deine Karten-Koordinaten, etc.". Ob das dann alles erfolgreich war, ist der View dann auch egal, sie zeichnet weiter einfach Sachen aus deinem Model.

Wenn du dann z.B. neben der Swing-Darstellung gleichzeitig auch noch eine Darstellung in OpenGL oder SWT machen könntest, hast du schon alles richtig gemacht ;)

Ist zumindest meine Meinung. ;)
 

Ähnliche Java Themen

Neue Themen


Oben