Hi,
ich muss noch mal darauf rum reiten. Vielleicht solltest Du Dein Design noch mal überdenken. Das mit dem Observerpattern von Laocoon ist sicher ein zielführender Vorschlag. Mein Tipp:
Du hast ganz unten eine Model-Klasse, die nix, aber auch gar nix von Views oder anderen Klassen weiss. Dafür erbst sie von Observable, bzw implementiert einen eigenen Benachrichtigungsmechanismus. Jeder Aufruf eines Setters in der Modelklasse löst ein Updateereignisaus.
Du machst eine zweite Klasse, den Controller (Adapter). Diese Klasse registriert sich sowohl beim Model, als auch beim View als Listener. (implementiert Observer, JListModel, oder was auch immer). Bei der Konstruktion der Klasse, gibst Du Referenzen, sowohl auf den View, als auch auf das Model mit und registrierst die Klasse bei beiden als Listener.
Wenn jetzt im View ein Ereignis feuert, wird der Controller benachrichtigt. Er hat aber eine Referenz auf das Model und kann den (die) geigneten Setter aufrufen. Umgekehrt wird jeder Setteraufruf im Model mit einem Updateereignis feuern. Der Contoller bekommt das mit und zeichnet den View neu. Voila.
Falls ich heute noch ein wenig Zeit habe, stelle ich eine simple Lösung ein.
Gruß
Edit:
Hier eine Lösung mit JFace
Zum Laufen lassen müsst ihr folgende Jars aus Eclipse einbinden
Die beiden org.eclipse.swt jars, org.eclipse.jface und org.eclipse.jface.text, sowie org.eclipse.core.command
Dann als SWT-Application starten.
Die Klasse ContentProvider ist der Controller, der die Zeilen aus dem Model liefert. In Swing würde er das ListModel implementieren. Statt des Listviewer könnte man dann genausogut eine JList nehmen. Er registriert sich beim Model als Listener und zeichnet den Viewer neu sobald sich irgendwas im Model ändert. Gleichzeitig registriert er sich beim viewer als doubleClickListener und läßt das doppeltgeklickte Schwein fressen.
Der Viewer hat keine Ahnung von der Struktur des Models und das Model keine Ahnung von der Struktur (oder gar der Existens) des Views.
Der Button "neues Schwein" soll nur zeigen, dass das Schreiben ins Model sofort ein Neuzeichnen des Views auslöst.
Die Tiere ;-)
Java:
package de.tutorials.tiere;
import java.util.Observable;
public class Schwein extends Observable{
private String name;
private int gewicht;
public Schwein() {
this("nobody");
}
public Schwein(String name) {
setName(name);
setGewicht(10);
}
public int getGewicht() {
return gewicht;
}
private void setGewicht(int gewicht) {
this.gewicht = gewicht;
fireEvent();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
fireEvent();
}
public void fressen() {
setGewicht(getGewicht() + 1);
}
@Override
public String toString() {
return "Schwein [Name="+getName()+", Gewicht=" + getGewicht() + "]";
}
private void fireEvent() {
setChanged();
notifyObservers();
}
}
Der Container für die Tiere (model)
Java:
package de.tutorials.tiere;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
public class SchweineModel extends Observable implements Observer{
private List<Schwein> schweine = new ArrayList<Schwein>();
public void add(Schwein e) {
e.addObserver(this);
schweine.add(e);
fireEvent();
}
public void clear() {
for(Schwein schwein: schweine)
schwein.deleteObserver(this);
schweine.clear();
fireEvent();
}
public void remove(Schwein o) {
o.deleteObserver(this);
schweine.remove(o);
fireEvent();
}
public Object[] toArray() {
return schweine.toArray();
}
public void update(Observable o, Object arg) {
fireEvent();
}
private void fireEvent() {
setChanged();
notifyObservers();
}
}
Der Controller (Adapter, Zwischenstück, wie auch immer)
Java:
package de.tutorials.tiere.provider;
import java.util.Observable;
import java.util.Observer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.Viewer;
import de.tutorials.tiere.Schwein;
import de.tutorials.tiere.SchweineModel;
public class SchweineContentProvider implements IStructuredContentProvider, Observer, IDoubleClickListener {
private ListViewer viewer;
public Object[] getElements(Object inputElement) {
SchweineModel model = (SchweineModel) inputElement;
return model.toArray();
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = (ListViewer) viewer;
this.viewer.removeDoubleClickListener(this);
this.viewer.addDoubleClickListener(this);
if (oldInput == null)
((SchweineModel) newInput).addObserver(this);
}
public void update(Observable o, Object arg) {
//plumpes Neuzeichnen durch neusetzten der Quelle. (geht auch besser ;-))
SchweineModel model = (SchweineModel) o;
viewer.setInput(model);
}
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Schwein schwein = (Schwein) selection.getFirstElement();
schwein.fressen();
}
}
Der Labelprovider liefert nur den String zum Object aus der Zeile, wäre in Swing Teil des ListModels
Java:
package de.tutorials.tiere.provider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
public class SchweineModelLabelProvider extends LabelProvider {
@Override
public Image getImage(Object element) {
return null;
}
@Override
public String getText(Object element) {
return element.toString();
}
}
Und zuletzt der View
Java:
package de.tutorials.gui;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import de.tutorials.tiere.Schwein;
import de.tutorials.tiere.SchweineModel;
import de.tutorials.tiere.provider.SchweineContentProvider;
import de.tutorials.tiere.provider.SchweineModelLabelProvider;
public class SchweineViewer {
private static final Display display = new Display();
private Shell shell = null;
private ListViewer viewer = null;
private SchweineModel model = null;
private Button button = null;
SchweineViewer() {
getViewer();
getButton();
dispatch();
}
private void dispatch() {
getShell().open();
while(! getShell().isDisposed()) {
if(! getDisplay().readAndDispatch())
getDisplay().sleep();
}
}
private static Display getDisplay() {
return display;
}
private Shell getShell() {
if(shell == null){
shell = new Shell(getDisplay());
shell.setLayout(new FillLayout());
}
return shell;
}
private ListViewer getViewer() {
if(viewer == null){
viewer = new ListViewer(getShell(), SWT.SINGLE);
viewer.setContentProvider(new SchweineContentProvider());
viewer.setLabelProvider(new SchweineModelLabelProvider());
viewer.setInput(getModel());
}
return viewer;
}
private SchweineModel getModel() {
if(model == null) {
model = new SchweineModel();
model.add(new Schwein("Piggy"));
model.add(new Schwein("Babe"));
}
return model;
}
private Button getButton() {
if(button == null) {
button = new Button(getShell(), SWT.PUSH);
button.setText("Neues Schwein!");
button.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
getModel().add(new Schwein());
}}) ;
}
return button;
}
public static void main(String[] args) {
new SchweineViewer();
}
}