MVC

G

Gast2

Gast
Also ich persönlich steh auf das Sun Tutorial MVC ... Moment ich krams mal raus:

Java SE Application Design With MVC

Ist nicht ganz einfach wegen der Verwendung von Reflection aber sehr flexibel und eine sehr lockere Bindung zwischen den Klassen.

Ich persönlich modifiziere das Muster jedoch gerne dahingehend, dass ich pro Controller nur ein Model habe und auch eine Reflection Getter Methode implementiere um neu hinzugekommene Views direkt aus dem Controller heraus zu initialisieren. (Update Property über Getter).

Vorteil: Die Views kennen das Model gar nicht und umgekehrt. Ist also eigentlich dann ein Model View Presenter/Mediator ...

Noch ein Vorteil: Die PropertyChange Support Klasse ist wesentlich einfacher zu nutzen, da keine Änderungen gefeuert werden, sollte der Wert auf den gleichen Wert gesetzt worden sein.

Nachteil: Model muss den Controller auch kennen um die definierten Properties zu unterstützen. Reflection ist potenziell fehleranfällig wenn man niacht aufpasst. Sollte aber kein Problem sein wenn man die JavaBean Guidelines einhält.

Die Muster haben alle ihre Vor und Nachteile. Welches bei dir am besten passt musst du selber entscheiden.
 
Zuletzt bearbeitet von einem Moderator:

Dit_

Bekanntes Mitglied
Vorteil: Die Views kennen das Model gar nicht und umgekehrt. Ist also eigentlich dann ein Model View Presenter/Mediator ...

Nachteil: Model muss den Controller auch kennen um die definierten Properties zu unterstützen. Reflection ist potenziell fehleranfällig wenn man niacht aufpasst. Sollte aber kein Problem sein wenn man die JavaBean Guidelines einhält.

ich glaube es ist ja gerade Sinn der Sache, dass die View das Modell kennt. Die ganze Swing-API ist auf diesem Prinzip aufgebaut. Es geht auch darum dass man Schnittstellen, Facaden, Observer usw verwendet. Ändert sich der Zustand des modells, muss es ja die View mitkriegen. d.h. irgendwie muss das Modell die View (als Observer) kennen.

Bsp:
Code:
TableModel.fireTableDataChanged();

Und wenn das Modell den Kontroller kennt, dann ist es ganz sicher kein MVC. Kontroller soll meiner Meinung nach Benutzereingabe an das Model weiterleiten. Es ist nicht die Aufgabe des Kontrollers die Gui zu aktualisieren... denke ich... :rtfm:

:oops:
 
G

Gast2

Gast
ich glaube es ist ja gerade Sinn der Sache, dass die View das Modell kennt. Die ganze Swing-API ist auf diesem Prinzip aufgebaut. Es geht auch darum dass man Schnittstellen, Facaden, Observer usw verwendet. Ändert sich der Zustand des modells, muss es ja die View mitkriegen. d.h. irgendwie muss das Modell die View (als Observer) kennen.

Bsp:
Code:
TableModel.fireTableDataChanged();

Und wenn das Modell den Kontroller kennt, dann ist es ganz sicher kein MVC. Kontroller soll meiner Meinung nach Benutzereingabe an das Model weiterleiten. Es ist nicht die Aufgabe des Kontrollers die Gui zu aktualisieren... denke ich... :rtfm:

:oops:

<Klugs*****Modus>

Ich hab ja auch gesagt, dass es eigentlich kein echtes MVC sondern eher ein
MVP ist bzw auch gerne Model View Mediator genannt ... Viele Wege führen
nach Rom.

Im übrigen werden die Views natürlich benachrichtigt. Es ist aber komplett
Event Driven und die Daten werden in den Events (PropertyChangeEvents)
transportiert.

Aber gut, dass du gerade behauptet hast Sun sei zu blöd so ein Muster zu
nutzen ... Ich bezog mich schließlich auf das Original Sun Tutorial ...

DIE haben bestimmt keine Ahnung von Java und Mustern ...

</Klugs*****Modus>

Noch ein Nachtrag (OriginalAuszug Wikipedia) :

Model-View-Presenter (Abkürzung: MVP; wörtlich etwa ‚Modell-Ansicht-Präsentator‘) ist ein Entwurfsmuster in der Softwareentwicklung, das aus dem Model-View-Controller (Abkürzung: MVC) hervorgegangen ist. Es beschreibt einen neuartigen Ansatz, um das Modell (engl. Model) und die Ansicht (engl. View) komplett voneinander zu trennen und über einen Präsentator (engl. Presenter) zu verbinden. Dabei steht neben einer deutlich verbesserten Testbarkeit auch die strengere Trennung der einzelnen Komponenten im Gegensatz zu MVC im Vordergrund.

Erstmals eingesetzt und genannt wurde dieses Entwurfsmuster in den 1990er-Jahren von IBM und Taligent. Martin Fowler formulierte jedoch im Jahre 2004 Model-View-Presenter nach seinem Verständnis. Seine Definition ist heute ausschlaggebend.
 
Zuletzt bearbeitet von einem Moderator:

Guardi

Bekanntes Mitglied
Ich steig mal kurz allgemein in die Diskussion ein.

Die Frage die sich ja mit oft stellt ist:
Darf ein Controller direkt die View ändern oder nicht?
Was ist wenn ein Nutzer bestimmten Aktionen auf der GUI macht, und das Zusammenspiel vieler Aktionen ergibt dann erst eine gültige Benutzereingabe. Beispielsweise werden Panels in einer bestimmten Reihenfolge vom Nutzer arrangiert. Das was wirklich in die Business-Logik soll, kommt aber erst später bzw. nach einer Vielzahl von User-Interaktionen.
Um die Frage selbst zu beantworten: Entweder ich nutze für sowas nochmal explizit Beans die sonst nichts mit der BL zu tun haben oder erlaube Controller die direkt die View modifizieren.

Nur noch mal so als Denkanstoss.
 

thomas86

Mitglied
Ich kenne das Tutorial von Sun, ich würde jedoch gerne ohne Beans arbeiten. Also erstma die Grundlagen richtig verstehen.

Nur eine Frage nebenbei: Ich habe jetzt eine ComboBox, bei der anhand eines gewählten Elements ein bestimmtes Controll angezeigt wird.

View: Mein Formular
Controller: stößt die Funktionen im View an, die entsprechende Controller anzeigt und ausblendet
Model: Model der ComboBox(DefaultComboBoxModel?)

Die View hat aber noch einige Buttons und ... also mehre ActionListener, wird für jedes ein Controll benötigt? Ich hänge hier etwas, kann mir wer bitte einen kleinen Anstoß geben
 
Zuletzt bearbeitet:
G

Gast2

Gast
Das Model ist ganz loose gekoppelt und ist unabhängig von Controller und View.
Den PropertyChangeSupport würde ich vermeiden und lieber einen eigenen machen.
Begründung:
Im PropertyChangeSupport ist eine ListenerListe d.h. die gleiche Componente könnte sich mehrmals im Model registrieren. Wenn man die Componente deregistrieren möchte wird aber nur EIN Listener weggenommen. Darum lieber Finger weg vom PropertyChangeSupport und lieber ein Gerüst wie bei EMF bevorzugen.

Ein anderer Denkansatz ist einen EventBus einzuführen an den sich jede View hängen kann.
 
G

Gast2

Gast
Das Model ist ganz loose gekoppelt und ist unabhängig von Controller und View.
Den PropertyChangeSupport würde ich vermeiden und lieber einen eigenen machen.
Begründung:
Im PropertyChangeSupport ist eine ListenerListe d.h. die gleiche Componente könnte sich mehrmals im Model registrieren. Wenn man die Componente deregistrieren möchte wird aber nur EIN Listener weggenommen. Darum lieber Finger weg vom PropertyChangeSupport und lieber ein Gerüst wie bei EMF bevorzugen.

Ein anderer Denkansatz ist einen EventBus einzuführen an den sich jede View hängen kann.

Jemandem der gerade anfängt, den Rat zu geben lieber etwas eigenes zu programmieren, anstatt etwas zu verwenden, was offiziell zur API gehört finde ich schon recht seltsam.

Eigentlich gilt immer: Erfinde nicht alles neu! Jemand anderes hat das wahrscheinlich schon besser gemacht. (Das gilt natürlich nicht immer)

Im Fall PropertyChangeSupport ist die Kritik zwar berechtigt, aber etwas selbst programmiertes hat dann vielleicht diesen Makel nicht aber dafür 10 andere...

ICh würde zunächst definitiv von einem Eigenkonstrukt abraten. Dann lieber ein anderes Muster verwenden, wo man den klassischen Weg geht.
 

thomas86

Mitglied
Was sollte denn für mich, als Anfänger, eine solide Lösung sein. Nach welchem Tutorial sollt eich arbeiten. Es gibt einfach zu viele verschiede Ansätze.
 
G

Gast2

Gast
Jemandem der gerade anfängt, den Rat zu geben lieber etwas eigenes zu programmieren, anstatt etwas zu verwenden, was offiziell zur API gehört finde ich schon recht seltsam.

Klar wenn man einen Fehler in einem Konstrukt weiß sollte man des trotzdem benutzen sehr sinnvoll API hin oder her ???:L...
Einfach anstatt eine Liste ein Set benutzen dann ist das schlimmste draußen und den rest vom code kann man kopieren ich denke das bekommt sogar ein Anfänger hin.

Außerdem heißt es nicht wenn man MVC benutzt dass man überall addPropertyChangeListener machen sollte. Daürber kannst mal mit Marco philosophieren ;)...
 
G

Gast2

Gast
Was sollte denn für mich, als Anfänger, eine solide Lösung sein. Nach welchem Tutorial sollt eich arbeiten. Es gibt einfach zu viele verschiede Ansätze.

Es gibt mehrere Interpretationen von MVC und mehrere Ableitungen weiterentwicklunge davon siehe MVA, MVP usw.
Fang einfach mal an, die Fragen/Probleme werden von selbst kommen ;)
 

Guardi

Bekanntes Mitglied
Ich würde erstmal den ganz klassischen Ansatz wählen ohne viel Schnick-Schnack.
Dazu brauchst du eigentlich erstmal nur MVC-Aufbau an sich und Observer-Pattern.
Fang erstmal nur damit an. Optimieren kann man hinterher immernoch.
 
G

Gast2

Gast
Das wurde in diesem Forum doch schon gefühlte 1000 mal besprochen schau dir die Thread doch an, da werden ALLE Fragen mit Beispielen sogar beantwortet ...
 

Guardi

Bekanntes Mitglied
Meinst du es in etwa so:
Model wird observed
View ist Observer
Controller kennt beide und steuert ...

Ja genau.

Und übrigens: Beans sind ja im Prinzip nur POJOs (Plain Old Java Objects), also Objekte mit Gettern und Settern und maximal noch Validierungslogik und sind nicht unbedingt mit Beans aus der Java EE Welt oder Spring gleichzusetzen, wo die Beans spezifisch eingebunden sind, aber dem selben Prinzip folgen. Ich finde es schon recht erstrebenswert nen Bean-Layer zu haben (inspiriert von z. B. JSP / JSF), dass relativ unabhängig vom Business-Layer ist.
 
G

Gast2

Gast
Hier ist mal ein Minimal Beispiel mit PropertyChangeSupport.

Denk daran dass anonymeListener auch Controller sind
 

Anhänge

  • mvc.jar
    12,1 KB · Aufrufe: 11

thomas86

Mitglied
Es geht gut voran mittlerweile, doch eine Frage habe ich noch.

In meiner main-Klasse wird nun das Model erstellt, danach wird der Controller erstellt und das Model übergeben. Der Controller erstellt dann den View und zeigt dieses an. Über das Observe-Pattern wird das View dann vom Model informiert, falls Daten geändert werden.

Ich habe es jetzt anhand eines Beispiels gemacht. Anhand einer gewählten Combobox-Items wird ein Controll angezeigt. Wie ist es jetzt wenn ich im gleichen Formular jetzt noch andere Model und Controller habe. Verwendet man dann einen MainController?

Hier mal mein Code
Das Model
Java:
public class PropertyModel implements PropertyModelInterface {
    private ArrayList Observers = new ArrayList();
    private String strProperty;
    
    @Override
    public void setSearchProperty(String strProperty) {
        this.strProperty = strProperty;
        notifyObservers(); 
    }
    
    public void notifyObservers(){
        for(int i = 0; i < Observers.size(); i++){
            PropertyObserverInterface o = (PropertyObserverInterface)Observers.get(i);
            o.setSearchProperty();
        }        
    }
    
    @Override
    public void registerObserver(PropertyObserverInterface o) {
        Observers.add(o);
    }

    @Override
    public void removeObserver(PropertyObserverInterface o) {
        int i = Observers.indexOf(o);
        if(i >= 0){
            Observers.remove(i);
        }
    }
    
    @Override
    public String getSearchProperty() {
        return this.strProperty;
    }  
}

Der Controller
Java:
public class PropertyController implements PropertyControllerInterface {
    MainView view;
    PropertyModelInterface model;
    
    public PropertyController(PropertyModelInterface model){
        this.model = model;
        view = new MainView(this, model);     
        view.setVisible(true);
    }

    @Override
    public void setSearchProperty(String strProperty) {
        model.setSearchProperty(strProperty); 
    }
}

Der View
Java:
public class MainView extends javax.swing.JFrame implements ActionListener,  PropertyObserverInterface {  
    private PropertyControllerInterface controller;
    private PropertyModelInterface model;

    /** Creates new form Main_View */
    public MainView(PropertyControllerInterface controller, PropertyModelInterface model) {        
        initComponents();
        
        this.controller = controller;
        this.model = model;
        model.registerObserver(this);

        cbSearchProperty.addActionListener(this);
        
        setTBActive();
    }  

@Override
    public void actionPerformed(ActionEvent e) {        
        if(e.getSource() == cbSearchProperty){
            controller.setSearchProperty(getSearchCBProperty());           
        }
    }
    
    public void setSearchProperty(){
        String strSearchProperty = model.getSearchProperty();
        
        if(strSearchProperty.equals("Name") || strSearchProperty.equals("Dateiname")){
            setTBActive();
        }

        if(strSearchProperty.equals("Kategorie")){
            setCBActive();
        }

        if(strSearchProperty.equals("Datum")){
            setTBDateActive();
        }
    }
/**
     * set search textbox active
     */
    public void setTBActive(){
        tbSearchText.setVisible(true);
        tbSearchDate.setVisible(false);
        cbSearchCategory.setVisible(false);                                              
    }

    /**
     * set search datetextbox active
     */
    public void setTBDateActive(){
        tbSearchText.setVisible(false);
        tbSearchDate.setVisible(true);
        cbSearchCategory.setVisible(false);
    }
    
    /**
     * set search combobox active
     */
    public void setCBActive(){
        tbSearchText.setVisible(false);
        tbSearchDate.setVisible(false);
        cbSearchCategory.setVisible(true);
    }
    
    // getter
    public String getSearchCBProperty(){
        return (String)cbSearchProperty.getSelectedItem();
    }
    
    public String getSearchTBValue(){
        return tbSearchText.getText();
    }
    
    public String getSearchTBDateValue(){
        return tbSearchDate.getText();
    }
    
    public String getSearchCBValue(){
        return (String)cbSearchCategory.getItemAt(0);
    }
    
    // setter   
    public void setSearchTBValue(String strValue){
        tbSearchText.setText(strValue);
    }
    
    public void setSearchTBDateValue(String strValue){
        tbSearchDate.setText(strValue);
    }
    
    public void setSearchCBValue(String strValue){
        cbSearchCategory.setSelectedItem((Object)strValue);
    }
 
G

Gast2

Gast
Java:
   @Override
    public void removeObserver(PropertyObserverInterface o) {
        int i = Observers.indexOf(o);
        if(i >= 0){
            Observers.remove(i);
        }
    }

1. Variablen klein
2. waum rufst du nicht direkt remove auf?
3. warum benutzt du kein set?


Ansonsten habe ich dir zu bedenken gegeben, dass ein ActionListener ebenfalls ein Controller ist (sogar der in MVC). Und dein eigener Controller eigentlich nur die Akutalisierung des model regelt, d.h.
1. Du benötigst den Controller gar nicht, da der ActionListener der Controller ist.
2. Du benutzt ihn weiter um später evntuelle Logik/Service aufrufe reinzubauen, aber du musst dir bewusst sein dass es kein eigentlich Controller im MVC ist, das ist DEIN ActionListener.
3. Wie du in deinem Controller siehst benötigst du die View gar nicht, dann schmeiß sie raus
 

thomas86

Mitglied
Ok, habe es nun geändert:

View
Java:
public class MainView extends javax.swing.JFrame implements PropertyObserverInterface {  
    private PropertyController controller;
    private PropertyModel model;

    /** Creates new form Main_View */
    public MainView(PropertyController controller, PropertyModel model) {        
        initComponents();
        
        this.controller = controller;
        this.model = model;
        model.registerObserver(this);

        cbSearchProperty.addActionListener(controller);
        
        setTBActive();
    } 
...
Controller:
Java:
public class PropertyController implements ActionListener {
    MainView view;
    PropertyModel model;
    
    public PropertyController(PropertyModel model){
        this.model = model;
        view = new MainView(this, model);     
        view.setVisible(true);
    }

    
    @Override
    public void actionPerformed(ActionEvent e) {        
        model.setSearchProperty(view.getSearchCBProperty());
    }
}
 
G

Gast2

Gast
Jop so würde es passen.

Aber die meisten Anwendungen, die ich gesehen haben nehmen deine 1. Variante mit anonymenListener. Weil es 1. schneller geht 2. eine bessere Übersicht bietet denk mal bei 5 Buttons wird die ganze Sache schon unnötig kompliziert 3. besser wartbar ist
 

thomas86

Mitglied
Also war die erste Variante doch besser? Naja, werde mir das mit den anonymen Listener mal anschauen.

Wie ist das jetzt wenn ich zu diesem View noch ein Controller+Model benötige. Würde ich einen MainController verwenden der die anderen initialisiert?
 
G

Gast2

Gast

Guardi

Bekanntes Mitglied
Jop so würde es passen.

Aber die meisten Anwendungen, die ich gesehen haben nehmen deine 1. Variante mit anonymenListener. Weil es 1. schneller geht 2. eine bessere Übersicht bietet denk mal bei 5 Buttons wird die ganze Sache schon unnötig kompliziert 3. besser wartbar ist

Ich kenns aus der Praxis das KEINE anonymen Listener genutzt werden weil es übersichtlicher ist und wesentlich besser wartbar sowie austauschbar. Mal von anderen interessanten Optionen abgesehen. Und warum soll die Sache bei 5 Buttons kompliziert werden? Sie wird eben durch anonyme Listener unübersichtlich.... und durch einen zentralen bekannten Controller wesentlich übersichtlicher...
 
G

Gast2

Gast
Ich kenns aus der Praxis das KEINE anonymen Listener genutzt werden weil es übersichtlicher ist und wesentlich besser wartbar sowie austauschbar. Mal von anderen interessanten Optionen abgesehen. Und warum soll die Sache bei 5 Buttons kompliziert werden? Sie wird eben durch anonyme Listener unübersichtlich.... und durch einen zentralen bekannten Controller wesentlich übersichtlicher...

Was wir denn daran unübersichtlich?
Java:
button1.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvente){
controller bzw. model.setProperty1(...);
}

});

button2.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvente){
controller bzw. model.setProperty2(...);
}

});

button3.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvente){
controller bzw. model.setProperty3(...);
}

});

button4.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvente){
controller bzw. model.setProperty4(...);
}

});

Kein if. schön sauber getrennt, jede Button hat seinen eigenen Controller. Kann schnell angepasst werden. Und alle Toolkit spezfischen Sachen sind in einer Schicht. Ist doch alles wunderbar!

Und dafür das alles jetzt in einen Controller reinzupflanzen und für die Listener evtl. noch eigene Klassen machen. Wofür soll das gut sein? Zeig mir dein Code...
 

Guardi

Bekanntes Mitglied
Nö ist nich mein Stil und wirds auch nie sein.
Ich habe in meinen Controllern immer gemeinsame Eigenschaften definiert die ich über abstrake Klassen usw. weitergebe. Warum sollte ich sowas über anonyme Klassen lösen? Ist mir zu viel Overhead und wesentlich unflexibler, da kannste mir erzählen was du willst ;)
Schon mal was von DRY gehört?
Ich kenns auch nicht anders aus der JEE Welt, da baut man auch nur konkrete Controller. Ich übernehme generell viele Konzepte aus JEE in meine einfachen SE Projekte.
 
G

Gast2

Gast
Hab jetzt kein Argument für deine Methode gesehen? Weder noch ein sinnvolles Beispiel, die deine Theorie bestätigt.
 

Guardi

Bekanntes Mitglied
Ok letztendlich ist da vieles auch subjektiv. In der Hinsicht muss ich zurückrudern.
Ich finds einfach auch schöner gekapselt wenn ich einem Controller z.B. meine Validierungsstrategie übergebe etc.
Wie gesagt, ich finde einfach dass deine Methode unübersichtlich ist. Kuddel-Muddel. In meiner View ist nur die View. Das heisst alles was zur Darstellung benötigt wird. In Controller-Klassen ist auch wirklich nur die Steuerung und fertig. Find ich von der Kapselung her einfach besser.
Über abstrakte Klassen und DI kannst du dir halt auch Controller mit Default-Operationen zusammenstöpseln. Benutz ich oft für Dialoge etc.

Wenn du die anonyme Lösung zufriedenstellend findest ist das ja ok, nur ich finds halt nicht schön :)
 

Guardi

Bekanntes Mitglied
Unheimlich konstruktiver Post @ SirLachdichweg, sowas braucht die Welt.

Ich meine die Validierung gegen die Eingabe. Ist eine E-Mail Adresse eine E-Mail Adresse? Oder dass eine Zahl Format X haben soll? Sowas geht bei mir nicht in die Business-Logik, bei dir etwa? Man kann solche Validierung z.B. in Beans machen, klar. Ist das schön? Hm ne. Man könnte aber auch einfach Validatoren schreiben für Eingabefelder die dann irgendwie 100% Reusability haben für etliche andere Projekte. Um genau zu sein is das der Standard in jedem besseren Web-Projekt in Java.
Du könntest einen Validator natürlich gerne AUCH noch mit in die View packen. Das fällt für mich aber nicht unter die Kategorie saubere Kapselung.

Ich kenne durchaus auch Leute die vertreten die Meinung immer dann wenn ichs kein zweites Mal mehr brauch mach ich es anonym, nur teile ich die Meinung halt in großen Projekten allein aus Wartbarkeitsgründen nicht...
Naja jeder soll nach seiner Fasson glücklich werden.

So, mehr will ich dann zu dem Thema auch nicht mehr sagen :)
 
G

Gast2

Gast
Ich meine die Validierung gegen die Eingabe. Ist eine E-Mail Adresse eine E-Mail Adresse? Oder dass eine Zahl Format X haben soll? Sowas geht bei mir nicht in die Business-Logik, bei dir etwa? Man kann solche Validierung z.B. in Beans machen, klar. Ist das schön? Hm ne. Man könnte aber auch einfach Validatoren schreiben für Eingabefelder die dann irgendwie 100% Reusability haben für etliche andere Projekte. Um genau zu sein is das der Standard in jedem besseren Web-Projekt in Java.

Also je nachdem gibt es ja schon Standards dafür siehe JSR-303 oder im EclipseRCP Umfeld ist es im Databinding mit drin, warum also das Rad neu erfinden?

Ich kenne durchaus auch Leute die vertreten die Meinung immer dann wenn ichs kein zweites Mal mehr brauch mach ich es anonym, nur teile ich die Meinung halt in großen Projekten allein aus Wartbarkeitsgründen nicht...

Klar sobald der Code öfters gebraucht wird, sollte er ausgelagert werden aber das gilt ja nicht nur für Listener (siehe Sonar,checkstyle usw. als Hilfstools )

Aus Wartbarkeitsgründen wird meistens zwischen GUI-Controller (Listener) und Anwendungscontroller unterschieden. In den Listener sollte daher sehr sehr wenig Code stehen.

btw. verwende ich für MVC eh am liebsten EMF oder einen EventBus
 
Zuletzt bearbeitet von einem Moderator:


Schreibe deine Antwort... und nutze den </> Button, wenn du Code posten möchtest...

Neue Themen


Oben