Swing Model an View binden

magna

Mitglied
Ich habe angefangen mich mit Swing zu beschäftigen und möchte nun eine kleine Anwendung erstellen und habe mich für die MVC-Architektur entschieden.

Wie ich nun bei Änderungen im Model auch die Views aktualisiere habe ich herausgefunden. Wie kann ich aber nun Werte des Models ändern, wenn ich Werte im View (Formular) ändere?

Zunächst hatte ich folgenden Code:

Java:
public class MyModel extends Observable implements Serializable {

    private Integer prop1 = null;

    public Integer getProp1() {
        return prop1;
    }

    public void setProp1(Integer prop1) {
	this.prop1 = prop1;
	setChanged();
    }

}

public class MyView extends JPanel implements Observer {

    JTextField prop1 = new JTextField(10);

    public MyView(final Observable model) {

	setBorder(BorderFactory.createEtchedBorder());
	setLayout(new GridBagLayout());

	// LazyGridBagConstraints ist eine eigene Klasse!	
	LazyGridBagConstraints c = new LazyGridBagConstraints();

	add(new JLabel("prop1"), c.nextCol());
	add(prop1, c.nextCol());

	model.addObserver(this);
	update(model, null);

    }

    public void update(final Observable observable, final Object object) {

	MyModel myModel = (MyModel) observable;

	prop1.setText(null == myModel.getProp1() ? null : myModel.getProp1().toString());

    }

}
Um auf die Änderungen in den Formularfeldern zu reagieren kam mir folgende Idee, von der ich aber noch nicht wirklich überzeugt bin:

Java:
public class MyForm extends MyView implements DocumentListener {

    public MyForm(final Observable model) {

	super(model);

	prop1.getDocument().addDocumentListener(this);

    }

    public void changedUpdate(final DocumentEvent e) {
        // TODO
    }

    public void insertUpdate(final DocumentEvent e) {
        // TODO
    }

    public void removeUpdate(final DocumentEvent e) {
        // TODO
    }

}

Was mich vor allem an diesem Ansatz stört ist die Tatsache, daß ich in den Methoden der Schnittstelle DocumentListener nicht weiß welche Eigenschaft des Models ich anzupassen habe. Alternativ könnte man natürlich für jede Eigenschaft des Models eine eigene Klasse schreiben die die Schnittstelle DocumentListener implementiert, aber das würde die Menge des Codes ja wahnsinnig aufblähen.

Wie macht man das also "richtig"? Für Anregungen Links und Verrisse meines Codes wäre ich dankbar!
 
Zuletzt bearbeitet:
Was du mit "welche Eigenschaft" meinst ist nicht ganz klar. Geht es darum, dass das Modell vielleicht ZWEI Methoden hat
setProp1(...)
setProp2(...)

Und es demnach ZWEI TextFields gibt, und beide den gleichen DocumentListener haben, der dann bei
Code:
public class MyForm extends MyView implements DocumentListener {
 
    public void changedUpdate(final DocumentEvent e) { update(); }
    public void insertUpdate(final DocumentEvent e) { update(); }
    public void removeUpdate(final DocumentEvent e) { update(); }

    private void update()
    {
        model.setProp[b] ja welche denn? [/b](...);
    }
}
nicht weiß, was er machen soll?
 
Was du mit "welche Eigenschaft" meinst ist nicht ganz klar. Geht es darum, dass das Modell vielleicht ZWEI Methoden hat
setProp1(...)
setProp2(...)
nicht weiß, was er machen soll?

Genau! Das wird aus meinem Code nicht wirklich klar, weil ich ihn der besseren Lesbarkeit wegen stark gekürzt habe. Mein reales Model besteht aus über 50 Eigenschaften (Properties), die zudem auch andere Datentypen haben können als Integer und auch als andere Formularfeldtypen als JTextField dargestellt sein können. Daß ich deshalb keinen einheitlichen Ansatz verfolgen kann ist mir klar, aber wie kann ich es vermeiden für jede dieser 50+ Eigenschaften einen eigenen DocumentListener (oder entsprechendes) zu schreiben?

Danke für deine schnelle Antwort.

Dein Ansatz mit der update()-Methode die in den anderen nur aufgerufen wird gefällt mir aber schonmal sehr gut.
 
EDIT: Ach, es geht auch um anderes als String und JTextField - naja, zumindest bräuchte man für jede "Kombination" die Auftaucht einen solchen Reflection-Listener (z.B. JCheckBox für Booleans, JTextField für String usw...)


Hmmm.. schwierig. Eigentlich wäre der Standardansatz da tatsächlich so, dass man für jede Eigenschaft einen Listener schreiben würde. Wenn diese Properties alle Strings sind, ist es IMHO schonmal... seltsam, dass das Modell da nicht eine Alternative anbietet. Wenn das Modell sowas hätte wie
model.setProperty("PROPERTY_NAME", propertyValue);
wäre das ganze ja einfach.

So... würde das IMHO erstmal nur mit reflection gehen, im Sinne von
Java:
nameTextField.add(createDocumentListener("setName"));
colorTextField.add(createDocumentListener("setColor"));
...

private DocumentListener createDocumentListener(String methodName)
{
    final Method method = holDirMitReflectionDieMethod(methodName);
    return new DocumentListener()
    {
        ... wie oben...

        private void update()
        {
            ...
            method.invoke(model, stringDerGesetztWerdenSoll);
        }
    };
}

Allerdings sollte man sich das genau überlegen: Durch Reflection hat man keine Compilezeit-Überprüfung mehr, die Refactoring-Mechanismen fallen weg ... man KANN damit viel Mist machen.
In diesem Fall könnte es aber OK sein, erstpart einem zumindest 49 repititive DocumentListener-Implementierungen...


EDIT2: Es gibt AFAIK auch Frameworks, die einem viel von dieser Arbeit abnehmen, aber ein konkretes weiß ich jetzt nicht. Wildcard kennt sich da glaubich sher gut aus ???:L
 
Zuletzt bearbeitet:
Super! Das ist doch schonmal ein Ansatz ...

Ich weiß, daß Apache Commons mit seinen BeanUtils die Reflection-API kapselt und so einen deutlich einfacheren Zugriff darauf ermöglicht. Hab mich mit Reflection nämlich noch nicht beschäftigt, meine nur gelesen zu haben, daß es nicht ganz trivial sei damit zu arbeiten.

Mich wundert nur, daß es keine gängige und einfache Lösung mit Java-Bordmitteln gibt, die eben dieses Wiederholen von Code vermeidet.

Dir aber ein dickes Danke!!!
 

Zurück
Oben