Observer Pattern: feuern bei neuer Referenz-Zuweisung?

Alex77

Mitglied
Angenommen die Klasse 'Spiel' verfügt über einen PropertyChangeSupport (ist also Observable).
Bei jeder Veränderung eines Members wird gefeuert (das ist das Pattern).
Aaaber ist es auch möglich zu feuern, wenn z.B. folgendes passiert:

Spiel s1 = new Spiel();
Spiel s2 = new Spiel();
s2 = s1; // kann hier automatisch gefeuert werden??? Oder geht das nur, wenn ich einen Wrapper benutze, der als
// einzigen Member Spiel hat und selbst Observable ist?
 
bedenke wer hier PropertyChangeSupport ist, wo sich Observer angemeldet haben, in Listen enthalten sind um sie zu informieren,
wenn das alles in einem Spiel-Objekt enthalten ist, dann ist ein anderes Spiel doch was völlig anderes, kennt keinen der alten Observer usw.,

ein Wrapper scheint das Ziel zu sein, ja
 
@SlaterB
Vielen Dank für die schnelle Antwort. Hm, ich beschäftige mit dem Pattern seit Wochen und immer wenn ich denke, dass ich alles verstand, kommt wieder das Gefühl nichts verstanden zu haben *lach* Oder ich bin einfach manchmal zu tief drin und sehe den Wald vor lauter Bäumen nicht mehr.

Letztlich habe ich nur folgendes alltägliches Problem:
Benutzer klickt auf den Button "Spiel laden" und die Variable des alten Spiels soll überschrieben werden. Da andere Klassen eine Referenz vom alten Spiel nutzen, sollen diese Klassen aktualisiert werden.

Oder ganz einfach gesagt: Wie teile ich dann z.B. einem Textfield und einer JTable mit, dass jetzt nach dem Laden neue Daten angezeigt werden sollen?
 
eine JTable hat eine Art Wrapper für die Daten, ein TableModel,
dort kann man setNewData() aufrufen und das (gleichgebliebene) Model berichtet seinem altbekannten Observer, der JTable

erstellt man dagegen ein neues Model muss man manuell
jTable.setModel(newModel);
aufrufen, damit die neuen Strukturen initialisiert werden (JTable meldet sich neu als Observer am neuen Model an)
 
@Michael + SlaterB
Vielen Dank für Eure Antworten. Ich muss jetzt mal in die Stadt und schaue dann nachher mal, ob ich das so umsetzen kann mit dem TableModel der JTable.
 
Ist jetzt leider einiges zu lesen für Euch. Aber ich wäre Euch echt dankbar, wenn ihr Euch das antut und meine 3 Fragen dazu beantworten würdet.

Habe mal an ein Modelprogramm gemacht. Statt eines TableModels weise ich der Table über einen Vector<Vector<Integer>> die Daten direkt zu (war für mich jetzt schneller zu machen).

Erklärung:
Zuerst die Klasse Spiel, die Dummy-Daten in Form eines Vector<Integer> mit 'getZahlen' und 'setZahlen' bereitstellt. In setZahlen wird firePropertyChange(..) ausgelöst.
Dieses wird empfangen von einem zuvor der Klasse Spiel zugewiesen AlexTableListener. Dieser schreibt die neuen Daten in die Tabelle.

Fragen:
1. Meintet Ihr das so mit dem Observer für die Klasse Spiel?
2. Sollte ich doch lieber ein AbstractTableModel verwenden? Im Moment sehe ich keinen Vorteil.
3. Ich habe PropertyChangeSupport der Klasse Spiel als Member zugewiesen. Ist das sinnvoll, oder doch lieber ableiten und 'extends PropertyChangeSupport' verwenden?

Java:
class Spiel {
    private  Vector<Integer> zahlen = new Vector<Integer>(); // Der eigentliche Inhalt der Klasse
    public PropertyChangeSupport propChange = new PropertyChangeSupport(this); 
    public static final String UPDATE = "update"; // String für firePropertyChange(..)
    Spiel() {
    }
    public Vector<Integer> getZahlen() { // hierüber holt sich AlexTableListener die Daten
        return zahlen;
    }
    public void setZahl(int i){   // in main() werden hiermit 2 Mal Neue Daten erzeugt, die in der Tabelle
// aktualisiert angezeigt werden.
        zahlen.removeAllElements();
        zahlen.add(i*2);
        zahlen.add(i*3);
        propChange.firePropertyChange(UPDATE, null, this); // übergebe Veränderungen
        // an AlexTableListener weiter
    }
}
public class NewJFrame extends javax.swing.JFrame {
    // .... sonstiger Code für JFrame stünde hier.....
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new NewJFrame().setVisible(true); // siehe ein paar Zeilen tiefer
            }
        });
    }

    private javax.swing.JScrollPane jScrollPane;
    private javax.swing.JTable jTable;

    public NewJFrame() {
        initComponents();
        Spiel spiel = new Spiel();  // ################### Hier geht es los 
        spiel.propChange.addPropertyChangeListener(
                new AlexTableListener(jTable, jScrollPane));
        spiel.setZahl(2); // In Tabelle die Zahlen: 4 und 6
        spiel.setZahl(5); // In Tabelle die Zahlen: 10 und 15

    }
}

class AlexTableListener implements PropertyChangeListener {
    private JTable table;
    private JScrollPane scrollpane; // ohne scheint kein repaint() möglich zu sein.
    
    public AlexTableListener(JTable table, JScrollPane scrollpane) {
        this.table = table;
        this.scrollpane = scrollpane;
    }
    public void propertyChange(PropertyChangeEvent evt) {
        if(evt.getNewValue() instanceof Spiel) { // ist das Event von Klasse Spiel?
            Spiel spiel = (Spiel)evt.getNewValue(); // neues Spiel durch firePropertyChange(..)
            Vector<Vector<Integer>> vec = new Vector<Vector<Integer>>(); // hier kommen die Tabellendaten rein
            vec.add(spiel.getZahlen()); // neue Zahlen holen
            Vector<String> head = new Vector<String>(); // Tabellen Kopfleiste
            head.add("Zahl1"); head.add("Zahl2"); // Tabellen Kopfleiste
            table = new JTable(vec, head); // Inhalt zufügen
            scrollpane.setViewportView(table); // Vorbereiten des Repaints.
            scrollpane.repaint(); // Neue Tabelle ausgeben
        }
    }
}
 
> 1. Meintet Ihr das so mit dem Observer für die Klasse Spiel?

sieht nach einer lauffähigen Variante aus, was für einen Aufbau du wählst ist dir aber ganz überlassen,
da wollte sicher keiner reinreden

> 3. Ich habe PropertyChangeSupport der Klasse Spiel als Member zugewiesen. Ist das sinnvoll, oder doch lieber ableiten und 'extends PropertyChangeSupport' verwenden?

nein, das extends brächte dir nur wenig Vorteile, etwa
> spiel.addPropertyChangeListener(..)
statt
> spiel.propChange.addPropertyChangeListener(..)
dafür wäre die Möglichkeit einer anderen Oberklasse verbaut,
bisher stehen wenig Vorteile gar keinem Nachteil gegenüber, da du sowieso keine andere Oberklasse hast,
allgemein ist aber ein Klassenattribut organisatorisch sauberer als eine Vererbung

> 2. Sollte ich doch lieber ein AbstractTableModel verwenden? Im Moment sehe ich keinen Vorteil.

schlecht ist auf jeden Fall new JTable(), solch ein Konstrukt kann schon mal MB an Speicher belegen und der ganze Austausch eine Sekunde dauern,
GUI-Komponenten wenn möglich nie ersetzen, aller höchsten zwischen mehreren Sichten hin- und herschalten

in diesem Fall ginge wahrscheinlich auch ohne eigenes Model
> DefaultTableModel model = (DefaultTableModel) table.getModel();
> model.setDataVector(vec, head);

fertig, nix zu scrollen oder sonst wie zu repaint(), das passiert automatisch, soweit nötig,
oder sonst ein neues Model erzeugen und in die Table setzen,
schlecht wäre in diesen Fällen nur, wenn überhaupt nirgendwo je eine JTable erzeugt wird..

wenn die Daten fertig als Vector vorliegen, brauchst du nicht unbedingt ein eigenes Model, richtig
 
@SlaterB
Herzlichen Dank für Deine ausführlichen Antworten zu allen drei Punkten.
Besonders der Punkt, dass ein '= new JTable(..)' zu Laufzeitproblemen führen kann, war mir nicht bewusst. Daher besonderen Dank für den DefaultTableModel Tipp. Und ich spare mir einen Parameter im Konstruktor wegen des Wegfalls der ScrollPane. Toll.

Dann werde ich mich erst einmal auch nicht mit dem TableModel beschäftigen. Sah mir doch etwas zeitaufwendig aus. In der Tat liegen meine Daten alle als Vectoren, Maps oder LinkedLists vor. Sollte also für das jetzige Programm reichen.

Noch mal vielen Dank!
 

Neue Themen


Zurück
Oben