Swing Kleines Programm mit SWING unter Verwendung von MVC

Curry

Aktives Mitglied
Moin Moin!

Ich möchte ein kleines Programm schreiben und beginne dafür mit der JAVA-Programmierung.

Bezüglich MVC habe ich mir diesen Artikel (Java SE Application Design With MVC) durchgelesen und das Beispiel studiert. Zur Übung habe ich einen Währungsumrechner, der auf dem Beispielprogramm basiert, geschrieben.

Darauf basierend habe ich begonnen mein kleines Programm zu schreiben, doch habe ich schon bald meine Grenzen aufgezeigt bekommen. In dem Beispielprogramm basieren alle View-Klassen auf der Klasse "AbstractViewPanel" die von "JPanel" abgeleitet ist. Im Großen und Ganzen ist das für mein Programm auch okay, aber das Hauptfenster möchte ich von JFrame ableiten und mit einer JMenuBar versehen.

Der Controller, welcher von der Klasse "AbstractController" abgeleitet ist, kann aber nur View-Klassen aufnehmen, die ihrerseits von der Klasse "AbstractViewPanel" abgeleitet wurden.

Das bereitet mir erhebliche Startschwierigkeiten, da ich nicht weiß, wie ich das Hauptfenster mit seiner Menüleiste in die MVC-Umgebung einbauen soll.

Freue mich auf eure Hilfe!

Hier der Quellcode der abstrakten Klasse "AbstractController":
Java:
package controller;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.ArrayList;

import view.AbstractViewPanel;
import model.AbstractModel;

public class AbstractController implements PropertyChangeListener {

	//  Vectors that hold a list of the registered models and views for this controller.
	private ArrayList<AbstractViewPanel> registeredViews;
    private ArrayList<AbstractModel> registeredModels;

    // Konstruktor
    public AbstractController() {
        registeredViews = new ArrayList<AbstractViewPanel>();
        registeredModels = new ArrayList<AbstractModel>();
    }
    
    /**
     * Binds a model to this controller. Once added, the controller will listen for all 
     * model property changes and propogate them on to registered views. In addition,
     * it is also responsible for resetting the model properties when a view changes
     * state.
     * @param model The model to be added
     */
    public void addModel(AbstractModel model) {
        registeredModels.add(model);
        model.addPropertyChangeListener(this);
    }
    
    /**
     * Unbinds a model from this controller.
     * @param model The model to be removed
     */
    public void removeModel(AbstractModel model) {
        registeredModels.remove(model);
        model.removePropertyChangeListener(this);
    }
    
    /**
     * Binds a view to this controller. The controller will propogate all model property
     * changes to each view for consideration.
     * @param view The view to be added
     */
    public void addView(AbstractViewPanel view) {
        registeredViews.add(view);
    }

    /**
     * Unbinds a view from this controller.
     * @param view The view to be removed
     */
    public void removeView(AbstractViewPanel view) {
        registeredViews.remove(view);
    }
    
    //  Used to observe property changes from registered models and propogate them on to all the views.
    
    /**
     * This method is used to implement the PropertyChangeListener interface. Any model
     * changes will be sent to this controller through the use of this method.
     * @param evt An object that describes the model's property change.
     */
    public void propertyChange(PropertyChangeEvent evt) {
        
        for (AbstractViewPanel view: registeredViews) {
            view.modelPropertyChange(evt);
        }
    }
    
    
    /**
     * Convienence method that subclasses can call upon to fire off property changes
     * back to the models. This method used reflection to inspect each of the model
     * classes to determine if it is the owner of the property in question. If it
     * isn't, a NoSuchMethodException is throws (which the method ignores).
     *
     * @param propertyName The name of the property
     * @param newValue An object that represents the new value of the property.
     */
    protected void setModelProperty(String propertyName, Object newValue) {
        
        for (AbstractModel model: registeredModels) {
            try {
            	System.out.println("Okay: setModelProperty");
                Method method = model.getClass().getMethod("set" + propertyName, new Class[] { newValue.getClass() });
                method.invoke(model, newValue);
                
            } catch (Exception ex) {
            	System.out.println("Fehler: setModelProperty");
            }
        }
    }
}

Und hier der Quellcode meiner "Main.java":
Java:
import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.WindowConstants;

import model.ExperimentModel;
import model.LogModel;
import view.LogViewPanel;
import view.MenuView;
import controller.DefaultController;

public class Main {

	public static void main(String[] args) {
	
		// Instanzieren der Models
		LogModel logModel = new LogModel();
		ExperimentModel experimentModel = new ExperimentModel();
		
		//model.initDefault();
		
		// Instanzieren der Controller
		DefaultController controller = new DefaultController();
		
		// Instanzieren der Views
		LogViewPanel logViewPanel = new LogViewPanel(controller);
		MenuView menuView = new MenuView(controller);
		
		// Verknüpfen des Controller mit den Views und den Models
		controller.addView(logViewPanel);
		//controller.addView(menuView); // der Methode kann nur ein AbstractViewPanel-Objekt übergeben werden
		controller.addModel(logModel);
		controller.addModel(experimentModel);
		
		JFrame rootFrame = new JFrame("TestTool");
		//rootFrame.setMenuBar(menuView); // der Methode kann nur ein JMenuBar-Objekt übergeben werden
		rootFrame.getContentPane().add(logViewPanel, BorderLayout.CENTER);
		rootFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		rootFrame.pack();
		rootFrame.setVisible(true);
	}
}
 
Der Controller, welcher von der Klasse "AbstractController" abgeleitet ist, kann aber nur View-Klassen aufnehmen, die ihrerseits von der Klasse "AbstractViewPanel" abgeleitet wurden.

Das bereitet mir erhebliche Startschwierigkeiten, da ich nicht weiß, wie ich das Hauptfenster mit seiner Menüleiste in die MVC-Umgebung einbauen soll.
das Hauptfenster und die Menüleiste gehören im MVC in den View (in seine konkrete Implementierung) und nur dorthin. Entweder der Controller, noch das Model sollten direkt auf das Hauptfenster oder die Menüleiste zugreifen, oder darüber überhaupt Bescheid wissen. Das MVC-Model kommuniziert über Events oder vergleichbare Mechanismen. Wenn also im View eine Aktion über ein Menu, Toolbar, Tastatur, Maus, JUnit-Test oder ähnliches ausgelöst wird, sollte diese Aktion vom View vorverarbeitet werden und als ein (vorher selber definiertes) Event an den Controller und/oder Model geschickt werden. Genauso auch umgekehrt. Ein View sollte im optimalen Fall nicht wissen, ob das Model jetzt auf einer CSV-Datei oder lokaler bzw. entfernter Datenbank arbeitet.

Slawa
 
Arbeite statt mit abstrakten Klassen mit Interfaces. Definiere ein ViewInterface, das alle View Klassen implementieren müssen. So kann man eine beliebige Klasse, egal ob sie von JPanel, JFrame oder sonst woher erbt jederzeit als View verwenden.
 
Ich würde in der GUI im anonymen Listener arbeiten und die View NICHT im Controller aufnehmen...
 

Zurück
Oben