Hilfestellung zu MVC-Umsetzung bzgl JTrees

UnkiDunki

Bekanntes Mitglied
Hi,

ich habe mich bis jetzt eigentlich noch nie wirklich mit MVC beschäftigt, aber der Anspruch wächst und man möchte ja schließlich auch sauberen/strukturierten Code schreiben. Jedenfalls habe ich mir jetzt schon einige MVC-Beispiele hier im Forum angeschaut, auch nachvollziehen können, aber wenn es dann um die eigene Umsetzung ging wie ein Ochs vorm Berg gestanden :) Also scheinbar nachvollzogen, aber schlussendlich wohl doch nicht ganz verstanden... :)

Daher bitte ich um eine kleine Hilfestellung zu folgendem Szenario. Ich hoffe, dieses dann auch auf meine anderen Bereiche übertragen zu können...

Folgendes Szenario:

Ich habe ein JPanel mit nem JTree in einer JScrollPane. Unter der Pane ein JTextField zur Anzeige des aktuellen Knotennamens und darunter drei Buttons zum "Hinzufügen", "Umbenennen" und "Löschen".

Der Baum soll und wird aus einer Datenbank gefüllt. Dafür habe ich dann u.a. die folgenden Klassen:

- MyTreeNode (abgeleitet von DefaultMutableTreeNode), erweitert durch NodeID und NodeName und entsprechenden Gettern/Settern
- MyJTreeModel (abgeleitet von DefaultTreeModel) mit Methoden zum rekursiven Knoteneinfügen aus DB
- MyJTree mit update-Methode, wo ich den Wurzelknoten ermittel und das Model (s.o.) setze, wobei ich den Wurzelknoten übergebe...

Außerdem habe ich MyJTree um einen TreeSelectionListener erweitert, wo immer der aktuell gewählte Knoten ermittelt und gesetzt wird.

So weit so gut?
Jetzt stellen sich mir natürlich die Fragen gemäß MVC...

1.) Wo füge ich jetzt zB. die Methoden renameCurrentNode(String pName), deleteCurrentNode(), addNode(pName) ein und von wo rufe ich sie auf?
Aktuell ist es so, dass sie sich in der Klasse MyJTree befinden. Die jeweiligen Methoden (s.o.) rufe ich im Panel in den den Buttons hinzugefügten ActionListenern auf a la tree.renameCurrentNode(tfCurrentNodeName.getText());

2.) Von wo setze ich den aktuellen Knotennamen auf das Textfeld? Aktuell ist es so, dass ich dieses auch in einem Listener im Panel mache bei entsprechendem Ereignis... genauso mit Aktivieren und Deaktivieren der Buttons, so dass bei leerem Namenstextfeld der Hinzufüge-Button z.B. deaktiviert ist.
Man könnte natürlich all diese Komponenten der MyJTree übergeben und in derem Listener diese Vorgänge ausführen, aber ich denke, dass das noch weniger dem MVC entspricht, richtig?

Ich würde mich sehr über Feedback zu meinen Punkten und vielleicht noch genaueren Fragen freuen!
Code würde ich natürlich auch posten, falls das nützlich ist, aber ich denke, dass das eher ein Aufbau-/Strukturproblem ist, was ich im Übrigen generell habe... Auch hinsichtlich dazu, würde ich Vorschläge, Anregungen etc. begrüßen :)

Danke schon mal im Voraus
 

André Uhres

Top Contributor
1.) Wo füge ich jetzt zB. die Methoden renameCurrentNode(String pName), deleteCurrentNode(), addNode(pName) ein und von wo rufe ich sie auf?
Die Methoden in einer Kontroller Klasse einfügen. Von der View rufst du sie auf.

2.) Von wo setze ich den aktuellen Knotennamen auf das Textfeld?
In der View Klasse, also dort, wo das Textfeld definiert ist, und zwar in einer Methode, die vom Model aufgerufen wird (die View registriert sich beim Model als Listener). Die Eingaben/Selektionen werden an den Kontroller weitergeleitet, der Kontroller informiert das Model, das Model informiert die View (ModelListener).

genauso mit Aktivieren und Deaktivieren der Buttons, so dass bei leerem Namenstextfeld der Hinzufüge-Button z.B. deaktiviert ist.
Die Information über leeres Feld wird an den Kontroller gegeben, der informiert das Model, das Model gibt Rückmeldung an die View damit diese den Hinzufüge-Button deaktiviert (immer der gleiche Mechanismus).
 

UnkiDunki

Bekanntes Mitglied
Hi André,

schon mal vielen Dank für deinen Beitrag. Ich bin gerade dabei das nachzuvollziehen, habe aber immer noch ein paar bzw. viele Schwierigkeiten.
Vielleicht könnten wir dein gepostetes Beispiel http://www.java-forum.org/allgemeines/91829-mvc.html] mal auf mein Szenario übertragen?

Die Kontroller-Klasse ist ja in meinem Beitrag noch nicht verhanden, die wäre also zu erstellen und u.a. mit den Methoden rename(), add() und delete() auszustatten.

Als View würde ich dann sowohl die MyJTree als auch das Panel einordnen, ist das richtig? Oder sind MyJTreeModel und MyJTree das Model?

Mein großes Problem ist vorallem das Integrieren des Models, also das Zusammenspiel dessen mit Kontroller und View. Wie es mit den beiden interagiert und für den jeweiligen Anwendungsfall zu erkennen, welches jetzt das Model ist... siehe oben :)

Die View steht also mit einem ModelListener mit dem Model in Verbindung... ändert sich was am Model, so sieht der Anwender dieses entsprechend der aufs Model angepassten View... und das Model mit dem Kontroller?

Mal das Szenario mit dem Entfernen eines Knotens...
Ich drücke den Button "Delete" und in seinem ActionListener wird die delete-Methode im Kontroller aufgerufen... das spielt ja jetzt das Model nicht mit...bzw. ja doch, da ich gucken muss, welcher Knoten aktuell ausgewählt ist... also mit einem JTreeSelectionListener im JTree? Diese Informationen holt sich dann der Controller oder die View?
Jedenfalls führe ich dann die Query zum Entfernen des Knotens im Controller aus und setze z.B. das JTreeModel neu... und zwar aus der Controller-Klasse, oder? Model wird neu gesetzt und View (JTree) ja automatisch informiert. Da bräuchte ich z.B. kein ModelListener...
Ok... ich habe es offentlich immer noch nicht verstanden :) Hoffe, dass du bzw. ihr noch ein wenig Geduld mit mir habt :)

Danke im Voraus
 

UnkiDunki

Bekanntes Mitglied
Ok... bei dem ganzen Feedback poste ich lieber mal ein bisschen Code. Ich habe mich mal an einen ganz einfach Logindialog gemacht, taste mich also so langsam ran...
Ich würde gerne mal eure Meinungen dazu hören. Was ist gut, was gibt es zu verbessern bzw. ob das für ne MVC-Lösung schon recht ordentlich wäre, etc.

Habe ein main-package für die Hauptklasse und dann halt view, model und controller-package. Ich denke mal, dass das ne recht sinnvolle Aufteilung ist :)

Java:
package main;

import controller.LoginController;
import view.LoginView;
import model.LoginModel;

public class LoginMVC {

	public LoginMVC(){

		LoginModel model = new LoginModel();
		LoginView view = new LoginView(model);
		LoginController controller = new LoginController(model, view);

		view.setVisible(true);
	}
}

Im Model gibt es nicht viel zu sehen... jedenfalls nicht in diesem Kontext, so dass ich mich dafür entschieden habe, dort zu prüfen, ob Logindaten korrekt sind. War da am überlegen, ob das nicht auch in den Controller gehört, aber da es ja um "Daten" geht, ist das im model doch richtig aufgehoben, oder?

Java:
package model;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import login.Crypt;
import database.DBConnection;

public class LoginModel {

	public LoginModel() {
		// TODO Auto-generated constructor stub
	}

	public boolean isValid(String pUsername, String pPassword) {

		boolean isValid = false;

		DBConnection dbCon = new DBConnection();

		try {
			dbCon.connect();
			Connection con = dbCon.getConnection();

			PreparedStatement stmt = con.prepareStatement("SELECT * FROM USERS WHERE username = ? AND password = ? LIMIT 1;");
			stmt.setString(1, pUsername);
			try {
				stmt.setString(2, (new Crypt(pPassword,true)).getString());
				System.out.println(""+(new Crypt(pPassword,true)).getString());
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			ResultSet rs = stmt.executeQuery();

			if (rs.next()) isValid = true;
			dbCon.close();

		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		return isValid;
	}
}

Java:
package view;

import gui.MyJDialog;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import model.LoginModel;

public class LoginView extends MyJDialog{

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	private JTextField tfUsername = new JTextField();
	private JPasswordField pfPassword = new JPasswordField();
	private JPanel panel = new JPanel();

	private LoginModel model;

	public LoginView(LoginModel pModel) {
		model = pModel;
		createGUI();
	}

	public LoginView(JFrame pFrame, String pString, boolean pB, LoginModel pModel) {
		super(pFrame, pString, pB);
		model = pModel;
		createGUI();
	}

	private void createGUI() {

		this.setSize(
				(int)(this.getToolkit().getScreenSize().width * 0.5),
				(int)(this.getToolkit().getScreenSize().height * 0.5)
				);

		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(final WindowEvent e) {
				exit();
			}
		});

		panel.setLayout(new GridBagLayout());
		panel.setBorder(new TitledBorder(""));

		this.addComponentInContainer((JComponent) this.getContentPane(), panel, 0, 0, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1.0, 1.0, new Insets(0,0,0,0));
		this.addComponentInContainer(panel, new JLabel("Username:"), 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 0.0, 0.0, new Insets(0,5,5,5));
		this.addComponentInContainer(panel, tfUsername, 1, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.EAST, 1.0, 0.0, 100, 0, new Insets(0,10,10,10));
		this.addComponentInContainer(panel, new JLabel("Password:"), 0, 1, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 0.0, 0.0, new Insets(0,5,5,5));
		this.addComponentInContainer(panel, pfPassword, 1, 1, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.EAST, 1.0, 0.0, 100, 0, new Insets(0,10,10,10));

		addToFocusQueue(tfUsername);
		addToFocusQueue(pfPassword);

		this.centralize();
		this.pack();
	}

	private void exit(){
		if (JOptionPane.showConfirmDialog(null, "Are you sure?", "Exit",JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) return;
		System.exit(0);
	}

	public String getUsername(){
		return tfUsername.getText();
	}

	public String getPassword(){
		return pfPassword.getText();
	}

	public void showError(String pTitle, String pText){
		JOptionPane.showMessageDialog(null,pText, pTitle,JOptionPane.ERROR_MESSAGE);
	}

	public void addLoginActionListener(ActionListener pListener) {
		pfPassword.addActionListener(pListener);
		pfPassword.setActionCommand("login");
	}

	public void addLoginKeyListener(KeyListener pListener) {
		tfUsername.addKeyListener(pListener);
	}
}

Java:
package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import model.LoginModel;
import view.LoginView;

public class LoginController {

	private LoginModel model;
	private LoginView view;

	public LoginController(LoginModel pModel, LoginView pView) {
		model = pModel;
		view = pView;

		view.addLoginActionListener(new LoginActionListener());
		view.addLoginKeyListener(new LoginKeyListener());
	}

	class LoginActionListener implements ActionListener{

		public void actionPerformed(ActionEvent arg0) {

			if(arg0.getActionCommand().equals("login")){
				if(!model.isValid(view.getUsername(), view.getPassword())) view.showError("Error", "Invalid username and/or password!");
				else view.dispose();
			}
		}
	}

	class LoginKeyListener implements KeyListener{

		public void keyPressed(KeyEvent arg0) {
			if(arg0.getKeyCode() == KeyEvent.VK_ENTER){
				view.changeFocusToNextComponent(arg0.getComponent());
			}
		}

		@Override
		public void keyReleased(KeyEvent arg0) {
		}

		@Override
		public void keyTyped(KeyEvent arg0) {
		}
	}

}

Also... was haltet ihr davon? Ist das ein guter Ansatz oder selbst in diesem einfachen Beispiel schon Strukturunterschiede zu einem vernünftigen MVC-Konzept?

Vielen Dank für euer Feedback :)
 

UnkiDunki

Bekanntes Mitglied
Ok... vergesst bitte den vorherigen Beitrag, denn ich möchte gerne wieder zum Ursprungsszenario zurück und dazu habe ich halt noch ein paar Verständnisprobleme, die ich leider immer noch mit mir herumtrage:

Also: Mal angenommen ich habe ein Panel mit einer JTable- und einer JTree-Komponente, die beide aus einer Datenbank gefüllt werden sollen. Beide funktionieren ja schon nach dem MVC-Prinzip. Setze ich z.B. die Models neu, so ändern sich ohne meine Zutun automatisch deren Views.
Sowohl Table als auch Tree habe ich mit eigenen Listenern ausgestattet, die bei Datenänderung und anderem reagieren. Diese habe ich in der View implementiert und kann so also auf Ereignisse dieser beiden Komponenten reagieren... z.B. mit (De)Aktivieren von Buttons etc.

Die meiner Meinung nach schwerste Frage ist: Was gehört eigentlich in das Model?
1.) Müsste ich korrekterweise TreeModel und TableModel in mein eigenes Model packen und mein Model dann mit getTreeModel() und getTableModel()-Methoden ausstatten, so dass ich in der View anfänglich die beiden Models auf die jeweiligeTable- und Tree-Komponenten setze, nachdem ich zuvor in der Controller-Klasse das Füllen der Models aus der DB veranlasse?

2.) Wie geschieht dann z.B. das Löschen eines Elementes aus der Tree. Das ganze soll nicht direkt in der Tree passieren, sondern in der DB und dann der Baum neugebaut werden...
Die Grundlage ist klar: Ein Button in der View, der via ActionListener die Methode deleteNode(nodesID) in der Controller-Klasse aufruft...
Aber dann? Die Löschen-Query in der Controllerklasse direkt ausführen und bei erfolgreichem Löschen, die Methode zum Neuerstellen in der Model-Klasse aufrufen oder gehört praktisch auch die DB-Ausführungen in die Model-Klasse... Bei letzterem hätte ich mir aber dann den Weg über Controller sparen können (höchstens vielleicht zwecks Parameterüberprüfung um dieses aus der View auszulagern).
Ist das ne Art "Philosophiefrage" oder gibt es da einen einzigen richtigen Weg bzw. DIE Struktur um eben beschriebenes auszuführen?

Ich habe bestimmt noch mehr Fragen bzgl MVC, aber ich denke, dass das erstmal meine wichtigsten Fragen waren...

Vielen Dank im Voraus :)
 

André Uhres

Top Contributor
Müsste ich korrekterweise TreeModel und TableModel in mein eigenes Model packen
und mein Model dann mit getTreeModel() und getTableModel()-Methoden ausstatten

Das ist sicherlich korrekt.

so dass ich in der View anfänglich die beiden Models auf die jeweilige Table- und Tree-Komponenten setze
Der besseren Trennung halber müsste die View den Kontroller fragen, er soll doch bitte das korrekte Model für JTable/JTree setzen.

Die Löschen-Query in der Controllerklasse direkt ausführen
Die Datenzugriffsebene ist im Model integriert, oder befindet sich eine Ebene unter dem Model. Die Anfrage zum Löschen wird vom Kontroller an das Model gerichtet. Bei erfolgreichem Löschen kann das Model sich selbst aktualisieren.
 

UnkiDunki

Bekanntes Mitglied
So... danke schon mal für die Geduld mit mir ;)

Ich habe jetzt noch eine Frage:

Ich möchte oben beschriebenes Konstrukt für verschiedene Akteure nutzen... Kunden, Lieferanten, etc.
Jetzt stellt sich mir die Frage wann bzw. wo ich genau akteurspezifisch werden muss. Sprich: Das Model und die View sind ja absolut akteurunabhängig zu programmieren und so umzusetzen, dass man von außen die unterschiedlichen Models, sofern sich z.B. ein TableModel des Kunden von dem des Lieferanten unterscheidet, von außen setzen kann. Genauso verhält es sich ja mit den unterschiedlichen Queries...

Meine Frage ist jetzt, wie man das am elegantesten realisiert:
1.) z.B. Möglichkeit via Konstruktur des Controllers oder ne Ebene drüber die unterschiedlichen Komponenten übergeben.
- Ok, das würde ich als nicht sehr vorteilhaft bezeichnen, da unter Umständen mehr als 10 verschiedene Parameter zu übergeben wären und ich glaube nicht, dass man das bei so einer Anzahl dann noch übergibt. Korrekt, oder?

2.) Eine Möglichkeit, die mir einfallen würde, die aber sehr unelegant klingt, ist nur einen Parameter zu übergeben, der bestimmt, von welchem Typ der Akteur jetzt sein soll... z.B. ein String mit "Customer"... Im Controller setze ich dann je nach Inhalt die passenden Queries, Models etc.
Wäre das ne Möglichkeit?

3.) Oder man könnte ja z.B. mit CustomerController extends Controller, etc. oder besser noch eine Ebene höher (?!?) arbeiten und dann in der abgeleiteten Klasse die Models etc. festlegen und dann via Setter an das parent übergeben.
Das würde dann ja heissen, pro Akteur ne neue Klasse, die sich nur darum kümmert, welche Komponenten in der View und im Model zu setzen sind.
Das bräuchte man ja bei Punkt 2.) z.B. nicht.

4.) Wahrscheinlich die eleganteste und beste Möglichkeit:
Ja... welche?!? :) Wie macht man es richtig? Wo und wie fängt die "Spezialisierung" auf die jeweiligen Akteure an?

Vielen Dank schon mal im Voraus für das Interesse :)
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
Wenn es darum geht, "constructor overload" zu vermeiden, könnte eine "supporting configuration class" helfen, wie sie hier vorgeschlagen wird: Avoid 'constructor madness'
Es gibt auch das "Builder Pattern". Aber daraus können sich solche "Monsteranweisungen" ergeben:
Student s2 = new StudentBuilder().name("Spicoli").age(16).motto("Aloha, Mr Hand").buildStudent();
Manche stehen darauf, mir tut das Auge weh :D
 

UnkiDunki

Bekanntes Mitglied
Danke für deinen ersten Tipp :) Ist wirklich ne gute Idee und einfach umzusetzen. So werde ich das machen :)
Builder Pattern sind einfach nur krank... auch nichts für mich muss ich dir sagen!
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Paty JFrame anfänger bräuchte hilfestellung AWT, Swing, JavaFX & SWT 7
E AWT Kleine Hilfestellung bei Bildern in Mediatracker AWT, Swing, JavaFX & SWT 7
K 2048 Umsetzung mit Gridpane / Stackpane und / Rectangle AWT, Swing, JavaFX & SWT 0
temi JavaFX Lösungsansatz für Umsetzung gesucht AWT, Swing, JavaFX & SWT 4
B Design Umsetzung AWT, Swing, JavaFX & SWT 1
M Tipps Zur Umsetzung einer GUI für ein Trading Card Game AWT, Swing, JavaFX & SWT 7
B Swing MVC Architektur Umsetzung AWT, Swing, JavaFX & SWT 9
T Touch GUI umsetzung AWT, Swing, JavaFX & SWT 6
S Umsetzung Stammbaumes AWT, Swing, JavaFX & SWT 10
M Swing MVC Pattern verstanden, aber Probleme bei der Umsetzung AWT, Swing, JavaFX & SWT 5
C Swing Hilfe bei umsetzung eines Stundenplans. AWT, Swing, JavaFX & SWT 7
R Frage nach der Umsetzung? AWT, Swing, JavaFX & SWT 8
E Swing Allgemeine Frage zur Struktierung und Umsetzung AWT, Swing, JavaFX & SWT 2
O Layout-Umsetzung AWT, Swing, JavaFX & SWT 11
Java_RY Bin Ratlos bzgl Malen in Swing AWT, Swing, JavaFX & SWT 5
W Swing bzgl. Tableskalierung/Darstellung einer Matrix AWT, Swing, JavaFX & SWT 8
M Frage zu KeyListener bzgl. JApplet AWT, Swing, JavaFX & SWT 3
hdi Swing Bzgl JFrame & modaler Dialog AWT, Swing, JavaFX & SWT 6
M Frage bzgl. Formular-Design. AWT, Swing, JavaFX & SWT 2

Ähnliche Java Themen

Neue Themen


Oben