Weitere Verständnisfragen zu MVC

UnkiDunki

Bekanntes Mitglied
Hi,

ich habe mal wieder einige Struktur-Fragen zur Umsetzung des MVC-Konzepts. Ein wirklich zufriedenstelliges Ergebnis werde ich wohl selbst nie hinbekommen, weil ich - was die grundsätzliche Strukturerkennung und deren Umsetzung angeht einfach ziemlich schlecht bin... Das gilt bei mir leider im Programmieren allgemein so. Wer da Tipps zur Verbesserung hat, darf mir gerne mal schreiben :)

MVC ist aber schon mal keine schlechte Übung und die macht es ja im Endeffekt aus :)

ALSO:
1.) Mir ist noch nicht ganz klar, welche Daten ich jetzt im Model halte und welche in der View. Kann man generell sagen, dass die View wirklich nur dazu dient darzustellen und sonst nichts? Bsp: Ich habe einige JComboBoxen mit einer Auswahl von verschiedenen Werten, z.B. unterschiedliche Währungen. Die Währungen befinden sich in der DB. Frage: Wie gelangen sie jetzt korrekt in die Comboboxen?

Ein möglicher Ansatz:
Ich habe im Model für jede "Datenbündel" (z.B. Währungen) eine Liste. Gemäß OOP hat mein Model also eine Liste mit Währungsobjekten. Diese Liste muss ich ja befüllen, was ich z.B. im Controller auslösen kann a la model.retrieveCurrencies() und im Model ein Select abgesetzt wird, was mir dann alle Währungen in der DB holt, instanziiert und dann auf die Liste setzt.
Für all diese Vorgänge habe ich ja einen eigenen ModelListener, der danach die View informiert (View registriert sich bei dem Model als Listener), dass die Währungen "geladen" wurden und daraufhin die entsprechende ComboBox gefüllt wird.

Wäre das so korrekt?

2.) Alle Listener für die Komponenten wie Textfelder oder Auswahllisten habe ich im Controller. Ist das wirklich so ratsam? Problem ist folgendes:
Bei einem ausgelösten Ereignis brauche ich nicht immer nur den Wert oder den Zustand der auslösenden Komponente, die ja als Event im Listener übergeben wird, sondern auch Zustände/Werte von anderen Komponenten. Dh. das ich ja eigentlich für alle relevanten Komponenten Getter benötigen würde, damit der Controller sich entsprechendes holen kann. Bei einem gewissen Viewumfang kann das aber schnell überhand nehmen und scheint mir auch nicht wirklich "sauber". Was wäre da die Alternative?
Listener in die View packen und dort drinnen via controller.doSomething() arbeiten, würde das ganze ja erleichtern...

So... das war erst mal meine kleine Fragestunde :) Ich würde mich freuen, wenn ich das ein oder andere beantworten bekommen könnte.
Möchte den Thread dann auch nur für weitere Fragen bzgl. MVC nutzen, die sich mir immer wieder stellen.
 
Zuletzt bearbeitet:
G

Gast2

Gast
zu 1) könnte man für den Anfang so machen... Später solltest du für die DB eine eigene Schicht einführen. Normal schreibst du einen Service, der auf die DB Schicht zugreift und dein Model füllt. Diesen Service kannst du im Controller ausführen.
zu 2) Ich würde nur anonyme Listener verwenden...
z.B.

Java:
public class View....

public init(){

JButton click = new JButton(new AbstractAction("Klick"){

public void actionPreformed(....){
controller.doClick();
}

});
}

Ich persönlich mach in einer Client Anwendung häufig eine Action als Controller und diese Action wird ausgeführt wenn der User was macht!
 

Tharsonius

Bekanntes Mitglied
Grundlegend ist es so ein bischen Geschmackssache wo man manche Sachen unterbringt.
Generell ist alles was mit der Anzeige zu tun hat in der View untergebracht. Man sollte eine View immer komplett austauschen können, ohne den Controller anfassen zu müssen.

Bezüglich der Währungen würde ich also sagen, der Controller besorgt sich die Daten aus der DB, bringt die in eine Liste und gibt diese Liste an die View. Die wiederum zeigt die an, so wie es gewünscht ist.


zu 2):
Listener gehören meiner Meinung nach zur View. Die haben so direkt im Controller nix verloren, schließlich hängen die direkt an den Buttons und was auch immer.

Gerade in Hinblick auf Austauschbarkeit sollte in der View der Listener stecken und der wiederum ruft die von Dir genannte Alternative controller.soSomething() auf.
 
G

Gast2

Gast
Bezüglich der Währungen würde ich also sagen, der Controller besorgt sich die Daten aus der DB, bringt die in eine Liste und gibt diese Liste an die View. Die wiederum zeigt die an, so wie es gewünscht ist.

So würde ich es nicht machen, wenn ich dir richtige verstanden habe... Was ist wenn du mehrere Views hast, die diese Information anzeigen sollen? Dann soll der Controller Events verschicken?
 

UnkiDunki

Bekanntes Mitglied
Hi,

erstmal vielen Dank euch beiden :)

SirWayne hat gesagt.:
zu 1) könnte man für den Anfang so machen... Später solltest du für die DB eine eigene Schicht einführen. Normal schreibst du einen Service, der auf die DB Schicht zugreift und dein Model füllt. Diesen Service kannst du im Controller ausführen.

"für den Anfang so machen"... nunja... lass uns das doch gleich richtig machen ;) Also für die DB eine eigene Schicht. Wie meinst du das genau?
Hättest du da ein Strukturbeispiel?

SirWayne hat gesagt.:
zu 2) Ich würde nur anonyme Listener verwenden...

Anonyme Listener erleichtern natürlich die Zuordnung zu den auslösenden Komponenten immens. Da spart man sich das Prüfen auf den Auslöser...
Ich hatte auch erst alle Listener in der View und habe das später geändert, da ich das in vielen Beispielen halt so gesehen hatte und von einer noch korrekteren Trennung gesprochen wurde...

Tharsonius hat gesagt.:
Generell ist alles was mit der Anzeige zu tun hat in der View untergebracht. Man sollte eine View immer komplett austauschen können, ohne den Controller anfassen zu müssen.

Der Controller ist doch aber fest mit der View gebunden und beide gehen da immer nur ne 1:1 Beziehung ein. Anders als beim Model, was wirklich unabhängig sein sollte und man auch mehrere von haben kann. Im Prinzip würde das ja dann heißen Model UND View sollen austauschbar sein können. Dann greift der Controller also am besten garnicht auf die View zurück. Das heisst ihn interessiert diese nicht. Nur das Model, was er durch seine Methoden ändert... VIEW -> CONTROLLER ja, CONTROLLER -> VIEW nein ??

Tharsonius hat gesagt.:
Bezüglich der Währungen würde ich also sagen, der Controller besorgt sich die Daten aus der DB, bringt die in eine Liste und gibt diese Liste an die View. Die wiederum zeigt die an, so wie es gewünscht ist.

Das würde sich meinem und ich glaube auch deinem widersprechen, oder?

SirWayne hat gesagt.:
So würde ich es nicht machen, wenn ich dir richtige verstanden habe... Was ist wenn du mehrere Views hast, die diese Information anzeigen sollen? Dann soll der Controller Events verschicken?
Mehrere Views hat man doch sowieso nicht: Habe gelernt, wie oben schon geschrieben, dass View und Controller immer ne 1:1 Beziehung eingehen sollen.

Mir fällt auch noch eine sehr grundlegende Frage ein:
Werden alle drei Klassen, also MV und C grundsätzlich noch mal in eine extra Klasse gepackt, worin die notwendigen Klassen instanziiert werden?
Also noch mal zur besseren Aufteilung...
Und wie sieht das aus, wenn man jetzt mehrere MVCs hat und sie unter einander kommunizieren müssen. Bleiben wir bei dem Währungsbeispiel.
Ich habe ja eine "Komponente 1", wo die Währungen in einer ComboBox angezeigt werden. In einer anderen (Komp2) sollen diese Währungen bearbeitet werden können. Wenn die Daten also in der DB geändert wurden, so muss ja Komponente2 Komponente1 benachrichtigen, die die ComboBox entsprechend aktualisiert...
Meine Frage: Geschieht das am besten in direkter Verbindung, also Komp2 besitzt im Model einen eigenen Listener, der vermeldet, wenn sie was geändert hat und das MVCKonstrukt1 registriert sich in dem Model als Listener und reagiert dann. Das wäre dann von Model1 zu Model2? Oder Controller1 zu Model2? Mhmm...
 
Zuletzt bearbeitet:
G

Gast2

Gast
Natrülich kann man mehrere Views haben warum denn auch nicht???? Wie kommst du auf ne 1:1 Beziehung???
Wie gesagt ich würde bei einer Client Anwendung anonyme Listener machen und in denen meine Controller(Actions) aufrufen die wiederum Zugriff auf was Modell haben... die View registriert sicht dann auf das Model, und reagiert darauf wenn sich das model ändert

Joa eine Datenbank schicht... Schau dir mal das DAO Pattern an!!! Oder du führst eine Peristenzschicht ein, aber ich weiß nicht wie gut deine Kentnisse sind und wie groß dein Programm ist ob sich eine Peristenzschicht lohnt. Schau dir auf jeden fall mal des DAO Pattern an...
 

UnkiDunki

Bekanntes Mitglied
DAO-Pattern angucken. Wird gemacht :)

Natrülich kann man mehrere Views haben warum denn auch nicht???? Wie kommst du auf ne 1:1 Beziehung???

Meine ich desöfteren gelesen zu haben... Habe ich selbst auch nicht so umgesetzt. Wahrscheinlich war das 1 Controller zu 1 View und nicht 1 View zu 1 Controller, was ja auch irgendwie Sinn macht ;) Das habe ich wohl missinterpretiert...
 
G

Gast2

Gast
DAO-Pattern angucken. Wird gemacht :)



Meine ich desöfteren gelesen zu haben... Habe ich selbst auch nicht so umgesetzt. Wahrscheinlich war das 1 Controller zu 1 View und nicht 1 View zu 1 Controller, was ja auch irgendwie Sinn macht ;) Das habe ich wohl missinterpretiert...

Es ist egal wie rum... Wie gesagt bei mir ist es so dass die meisten Controller sind und die können in soviele views wie eben benötigt.
Ich mach mal ein kleines Beispiel, schau nicht so auf den Code ist mal kurz auf die schnelle hingeklatscht, soll ja nur als Beispiel und Anregung dienen!!
package Controller:
Java:
package controller;

public abstract class Action {
	
	public abstract void run();

}
Java:
package controller;

import model.Kunde;
import model.KundenList;

public class CreateKundeAction extends Action{


	private static KundenList kundenList = new KundenList();
	private Kunde newKunde;


	@Override
	public void run() {
                // hier kommt nun die Verbindung zur DB und zu deiner DAO schicht und Service schicht hin
		kundenList.addKunde(newKunde);
	}

	public KundenList getKundenList() {
		return kundenList;
	}


	public void setNewKunde(Kunde newKunde) {
		this.newKunde = newKunde;
	}



	public Kunde getNewKunde() {
		return newKunde;
	}

}
Model package:
Java:
package model;

public class Kunde {

	private String name;

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
	
	@Override
	public String toString() {
		return name;
	}
}

Java:
package model;


import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;


public class KundenList  {
    
    private List<Kunde> kundeList = new ArrayList<Kunde>();
	protected transient PropertyChangeSupport listeners = new PropertyChangeSupport(this);
    
    
    
    /**
     * Adds a property-change listener.
     * @param l the listener
     */
    public void addPropertyChangeListener(PropertyChangeListener l){
        if (l == null) {
            throw new IllegalArgumentException();
        }
        this.listeners.addPropertyChangeListener(l);
    }
    
    
    public void removePropertyChangeListener(PropertyChangeListener l){
        this.listeners.removePropertyChangeListener(l);
    }
    
    /**
     * Notificates all listeners to a model-change
     * @param prop the property-id
     * @param old the old-value
     * @param newValue the new value
     */
    protected void firePropertyChange(String prop, Object old, Object newValue){
        if (this.listeners.hasListeners(prop)) {
            this.listeners.firePropertyChange(prop, old, newValue);
        }
    }

    public void addKunde(Kunde o) {
        this.kundeList.add(o);
        // model has changed --> fire
        firePropertyChange("KUNDE_ADD", null, o); //$NON-NLS-1$
    }
    
    public void remove(Kunde o) {
        this.kundeList.remove(o);
//      model has changed --> fire
        firePropertyChange("KUNDE_REMOVE", o, null); //$NON-NLS-1$
    }
    
    public List<Kunde> getKundeList() {
		return new ArrayList<Kunde>(kundeList);
	}


}
View package:
Java:
package view;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;

import model.Kunde;
import controller.CreateKundeAction;

public class CreateView extends JPanel{

	private CreateKundeAction action;
	private JTextField  field;
	public CreateView(){
		super();
		setBorder(BorderFactory.createTitledBorder("Kunde anlegen"));
		action = new CreateKundeAction();
		field = new JTextField(25);
		add(field);
		add(new JButton(new AbstractAction("Anlegen") {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Kunde k = new Kunde();
				k.setName(field.getText());
				action.setNewKunde(k);
				action.run();
				field.setText("");
			}
		}));
		
	}
}

Java:
package view;

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;

import controller.CreateKundeAction;

public class LastView extends JPanel implements PropertyChangeListener{

	private CreateKundeAction action;
	private JLabel jLabel;
	
	public LastView(){
		super();
		setBorder(BorderFactory.createTitledBorder("Letzter Kunde"));
		action = new CreateKundeAction();
		action.getKundenList().addPropertyChangeListener(this);
		jLabel = new JLabel();
		add(jLabel, BorderLayout.CENTER);	
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if(evt.getPropertyName().equals("KUNDE_ADD")){
			jLabel.setText(evt.getNewValue().toString());
		}
		
	}
}

Java:
package view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import controller.CreateKundeAction;

public class ListView extends JPanel implements PropertyChangeListener{

	private CreateKundeAction action;
	private DefaultListModel listModel;
	private JList jList;
	
	public ListView(){
		super(new FlowLayout());
		setBorder(BorderFactory.createTitledBorder("Alle Kunden"));
		action = new CreateKundeAction();
		action.getKundenList().addPropertyChangeListener(this);
		listModel = new DefaultListModel();
		jList = new JList(listModel);
		jList.setPreferredSize(new Dimension(150,150));
		add(new JScrollPane(jList));	
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if(evt.getPropertyName().equals("KUNDE_ADD")){
			listModel.addElement(evt.getNewValue());
		}
		
	}
}

Java:
package view;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main extends JFrame{
	
	public Main(){
		super("Beispiel MVC");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JPanel createView = new CreateView();
		JPanel listView = new ListView();
		JPanel lastView = new LastView();
		
		add(createView, BorderLayout.SOUTH);
		add(listView, BorderLayout.CENTER);
		add(lastView, BorderLayout.NORTH);
		
		pack();
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				new Main().setVisible(true);	
			}
		});
	}

}

Du könntest die Action auch nur einmal erzeugen und jeder View mitgeben was ich aber bei mehreren View unpraktisch finde...
 

Anhänge

  • mvc.jar
    11,4 KB · Aufrufe: 8
Zuletzt bearbeitet von einem Moderator:

Oben