# Abspeichern eines kompletten JPanels



## Nachtfalke (2. Jan 2012)

Hallo zusammen,

ich habe in meiner Anwendung ein JPanel auf dem diverse Objekte platziert sind. Nun würde ich gerne das gesamte JPanel beim Beenden der Anwendung abspeichern, um es beim nächsten Start wieder laden und anzeigen zu können. Dummerweise sind bei den enthaltenen Objekten solche dabei, die sich nicht serialisieren lassen. Gibt es noch eine andere Möglichkeit, das Problem zu lösen?


----------



## André Uhres (2. Jan 2012)

Hallo Nachtfalke,

bei MVC Objekten müssen wir lediglich das Model abspeichern. Ist es nicht serialisierbar, kann es in der Regel durch Erweiterung serialisierbar gemacht werden.

Gruß.
André


----------



## GUI-Programmer (2. Jan 2012)

Wenn du jetzt dennoch noch vorhast die ganzenKomponenten abzuspeichern, musst du halt die get(Component comp, int index) aufrufen und die entsprechenden Komponenten abspeichern, sowie die Getter die das JPanel bespreichen - eine mühsame und meiner und wahrscheinlich auch André Uhres Meinung nach sinnlose und unnötige Aufgabe.


----------



## Nachtfalke (2. Jan 2012)

Was sind MVC-Objekte? Und wie speichere ich das Model?


----------



## Gast2 (2. Jan 2012)

Nachtfalke hat gesagt.:


> Was sind MVC-Objekte? Und wie speichere ich das Model?



Mach mal langsam. MVC ist ein Design Pattern. Klassen die nach diesem Design Pattern konzipiert werden, bze die Objekte dieser Klassen, könnte man MVC Objekte nennen. Finde ich aber ehrlich gesagt blödsinn. 

Die Objekte die du dann hast wären Models Controller Klassen (Meistens durch Frameworks ersetzt) und die GUI Klassen. 

Les dir mal was dazu durch und mach mal ein paar einfache Übungen zum Thema. Wenn das läuft kannst du übers speichern nachdenken. Vorher nit. Man lernt ja auch erst laufen und dann sprinten.


----------



## GUI-Programmer (2. Jan 2012)

kappesf hat gesagt.:


> Les dir mal was dazu durch und mach mal ein paar einfache Übungen zum Thema.



Wie etwa meine Art mit MVC umzugehen: In MVC Ein JFrame, mehrere JPanels
Wobei du dann halt die Speichermechanismen im Model implementieren musst. D.h. du musst alle Werte abspeichern, z.B. bei wenigen Werten per Properties (z.B. über XML), und diese bei jeden Programmstart aus diesen Properties laden und dem Werten entsprechend deine GUI, also deine Panels mit Komponenten aufbauen.


----------



## Camino (2. Jan 2012)

Nachtfalke hat gesagt.:


> Was sind MVC-Objekte? Und wie speichere ich das Model?


Bei MVC hast du eine Trennung von Model (enthält die Daten) und View (Ansicht, GUI). Normalerweise baut sich die View nach den Daten des Models auf, bzw. aktualisert sich, wenn sich die Daten im Model geändert haben. Deshalb brauchst du wahrscheinlich nur diese Daten zu speichern und beim Neustart diese wieder einzulesen und die View zu aktualisieren.


----------



## André Uhres (2. Jan 2012)

Nachtfalke hat gesagt.:


> Was sind MVC-Objekte? Und wie speichere ich das Model?



Die Swing GUI Komponenten sind z.B. in der Regel nach dem MVC Prinzip aufgebaut. JTable hat ein TableModel und eine TableUI.  Aus der Sicht der MVC-Struktur implementiert TableModel das "Model", TableUI die "View" und JTable den "Controller" (mit "MVC-Objekt" meine ich die ganze Komponente). 

Man kann z.B. mit "XMLEncoder" eine JTable speichern und mit XMLDecoder wieder laden, indem man in Wirklichkeit nur das Model speichert und ladet. 

Gruß,
André


----------



## Schandro (2. Jan 2012)

Bin auch GUI-Programmers Meinung, überleg dir lieber was du wirklich abspeichern musst und mach das in deinem eigenen Format in XML o.Ä., ist denke ich sehr viel sinnvoller und du lernst dadurch auch mehr als einfach alle Components zu serialisieren


----------



## André Uhres (2. Jan 2012)

Schandro hat gesagt.:


> Bin auch GUI-Programmers Meinung, überleg dir lieber was du wirklich abspeichern musst


Das MVC-Konzept setzt ja gerade diese Überlegung voraus.



Schandro hat gesagt.:


> und mach das in deinem eigenen Format in XML o.Ä., ist denke ich sehr viel sinnvoller und du lernst dadurch auch mehr



Die Entwicklung eines sinnvollen Models ist im vorliegenden Fall wohl wichtiger als die Art des Abspeicherns, denn die gespeicherten Daten dienen ja nur der Wiederherstellung der GUI, wenn ich Nachtfalke richtig verstanden habe.

Gruß,
André


----------



## Schandro (2. Jan 2012)

André Uhres hat gesagt.:


> Die Entwicklung eines sinnvollen Models ist im vorliegenden Fall wohl wichtiger als die Art des Abspeicherns, denn die gespeicherten Daten dienen ja nur der Wiederherstellung der GUI, wenn ich Nachtfalke richtig verstanden habe.


Eine sinnvolles Model ist wohl die Grundlage für ein eigenes abspeichern anstatt alles von serialize machen zu lassen


----------



## André Uhres (2. Jan 2012)

Schandro hat gesagt.:


> Eine sinnvolles Model ist wohl die Grundlage für ein eigenes abspeichern anstatt alles von serialize machen zu lassen



Ein sinnvolles Model ist die Grundlage sowohl für ein eigenes Abspeichern als auch für ein sinnvolles Serialisieren. Ein eigenes Abspeichern bringt aber nach aktueller Sachlage für die Anwendung keinen Vorteil .

Gruß,
André


----------



## Nachtfalke (2. Jan 2012)

GUI-Programmer hat gesagt.:


> Wie etwa meine Art mit MVC umzugehen: In MVC Ein JFrame, mehrere JPanels
> Wobei du dann halt die Speichermechanismen im Model implementieren musst. D.h. du musst alle Werte abspeichern, z.B. bei wenigen Werten per Properties (z.B. über XML), und diese bei jeden Programmstart aus diesen Properties laden und dem Werten entsprechend deine GUI, also deine Panels mit Komponenten aufbauen.



Jetzt habe ich verstanden was ihr meint. Ich hatte mir schon gedacht, daß ich um eigene Speichermechanismen nicht herumkomme, aber ich hatte gehofft, daß es etwas ähnlich einfaches gibt wie die Serialisierung.


----------



## André Uhres (2. Jan 2012)

Nachtfalke hat gesagt.:


> Jetzt habe ich verstanden was ihr meint. Ich hatte mir schon gedacht, daß ich um eigene Speichermechanismen nicht herumkomme..



Ich habe ja gezeigt, wie Du um eigene Speichermechanismen herum kommen kannst . Insofern glaube ich nicht, dass Du alles verstanden hast ...

Gruß,
André


----------



## Nachtfalke (2. Jan 2012)

Ich habe es jetzt doch nochmal mit Serialisierung versucht, indem ich zunächst mal feststellen wollte, welche der Komponenten nicht serialisierbar sind. Das erste Problem trat bei einer JTable auf. Tante Google hat mir dann verraten, daß JTables im Gesamten nicht serialisierbar sind. Ist auch logisch, da das Speichern der GUI keinen Sinn machen würde. Daraufhin habe ich versucht, nur das Model der Table zu serialisieren. Das funktioniert aber auch nicht. Kann das an meinem Model liegen? 


```
public class StatTableModel extends DefaultTableModel implements Serializable {

  private final Integer STEP = 5;
  public FormatData[][] cellFormat = new FormatData[10][10];

  private FormatData[][] resizeArray(FormatData[][] array, Integer cols, Integer rows) {
    FormatData[][] help = new FormatData[array.length + cols][array[0].length + rows];
    for (int i = 0; i < array.length + cols; i++) {
      for (int j = 0; j < array[0].length + rows; j++) {
        help[i][j] = new FormatData();
      }
    }
    System.arraycopy(array, 0, help, 0, array.length);
    return help;
  }

  @Override
  public void addColumn(Object colName, Object[] data) {
    super.addColumn(colName, data);
    if (getColumnCount() >= cellFormat[0].length) {
      cellFormat = resizeArray(cellFormat, 0, STEP);
    }
  }

  @Override
  public void addRow(Vector data) {
    super.addRow(data);
    if (getRowCount() >= cellFormat.length) {
      cellFormat = resizeArray(cellFormat, STEP, 0);
    }
  }

  public StatTableModel() {
    super();
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 10; j++) {
        cellFormat[i][j] = new FormatData();
      }
    }
  }
}
```

Die Klasse FormatData implementiert Serializable.


----------



## bERt0r (2. Jan 2012)

Ja kann es, weil in deinem Model EventListener gespeichert werden, die meines Wissens nach nicht serializable sind. Kann mich aber auch täuschen. Am einfachsten ist es, du überschreibst einfach folgende Methoden:

```
private void writeObject(java.io.ObjectOutputStream out)
     throws IOException
 private void readObject(java.io.ObjectInputStream in)
     throws IOException, ClassNotFoundException;
 private void readObjectNoData() 
     throws ObjectStreamException;
```
Das heisst, du kümmerst dich manuell darum, welche Daten auf den outputstream geschrieben werden bzw. gelesen werden.
Discover the secrets of the Java Serialization API


----------



## André Uhres (3. Jan 2012)

Nachtfalke hat gesagt.:


> Daraufhin habe ich versucht, nur das Model der Table zu serialisieren. Das funktioniert aber auch nicht.



Du musst nur noch die fehlenden Getter/Setter zum StatTableModel hinzufügen:

```
public Vector getColumnIdentifiers() {
    return columnIdentifiers;
}
public void setDataVector(Vector dataVector) {
    this.dataVector = dataVector;
}
```
Danach funktioniert es ohne Weiteres mit XMLEncoder/XMLDecoder:

```
XMLEncoder o = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(filename)));
o.writeObject(table.getModel());
o.close();
...
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream(filename)));
table.setModel((TableModel) d.readObject());
d.close();
```

Gruß,
André


----------



## Nachtfalke (3. Jan 2012)

Also das mit der Serialisierung wird wohl nix:

Bug ID: 4891518 JTable SizeSequence Serialization Error

Und auf die Verwendung von setRowHeight kann ich nicht ohne weiteres verzichten


----------



## bygones (3. Jan 2012)

hab ich was verpasst ?!

warum musst du deine kompletten Swing elemente serialisieren und nicht einfach die Daten die die Elemente anzeigen ?!


----------



## Nachtfalke (3. Jan 2012)

Die Daten liegen im Tablemodel und das ist aufgrund des Bugs nicht serialisierbar.


----------



## Gast2 (3. Jan 2012)

Nachtfalke hat gesagt.:


> Die Daten liegen im Tablemodel und das ist aufgrund des Bugs nicht serialisierbar.



Da ist auch dein Denkfehler. Du brauchst ein Model (ein eigenes) in dem du die für DICH relevanten Daten ablegst. Deine GUI (respektive das JTable) stellt diese Daten für dich dar! WIE der JTable das widerum macht ist vollkommen egal. Das ist in diesem Fall zwar auch ein Model, jedoch nicht dein eigenes Program Model! 

Erstelle dir also eine eigene Model Klasse in der du die Daten vorhälst. Deine GUI verbindet sich mit dem Model und fragt die Daten ab. (Stichwort MVC). Der JTable muss dann natürlich deine Model Daten so übersetzen, dass sie in sein eigenes Model passen. Das JTable Model gehört aber dennoch zur GUI und somit zum VIEW im MVC.

Dein Model kannst du dann speichern wie immer du es für nötig hälst. Datenhaltung wäre dann von der Anzeigelogik getrennt. 

Stell dir nur mal vor du wechselt aus irgendeinem Grund die GUI Bibliothek. Dein Backbone (Daten) bleibt dann gleich. Nur die Darstellung verändert sich.


----------



## André Uhres (3. Jan 2012)

Hallo Nachtfalke,

wenn Du die Klasse "SizeSequence" abspeichern willst, musst Du sie kopieren und anpassen. 

Alternativ kannst Du vielleicht die Zeilenhöhe aus anderen Daten ableiten oder in die Zeilendaten als zusätzliche Eigenschaft einfügen, die nur benutzt wird, um die Höhe der betreffenden Zeile einzustellen.

Gruß,
André


----------



## Nachtfalke (3. Jan 2012)

André Uhres hat gesagt.:


> Hallo Nachtfalke,
> 
> wenn Du die Klasse "SizeSequence" abspeichern willst, musst Du sie kopieren und anpassen.
> 
> ...



Genauso hatte ich mir das gedacht. Inzwischen funktioniert die Serialisierung einwandfrei. Mittels dieser Methode:


```
public void saveStatistics() {
    
    int i;
    
    try
    {
      FileOutputStream file = new FileOutputStream("statistik.ser");
      ObjectOutputStream o = new ObjectOutputStream( file );
      System.out.println("Writing objects to disk ...");

      o.writeObject(statistikPanel);
      o.close();
    }
    catch ( IOException e ) { System.err.println( e ); }
  }
```

speichere ich das entsprechende Panel. Mittels dieser Methode:

```
public JScrollPane loadStatistics() {
    
    JScrollPane newPanel = new JScrollPane();
    try
    {
      System.out.println("Reading objects ...");
      FileInputStream file = new FileInputStream("statistik.ser");
      ObjectInputStream o = new ObjectInputStream(file);
      newPanel = (JScrollPane) o.readObject();
      o.close();
    }
    catch ( IOException e ) { 
      System.err.println( e ); 
    }
    catch ( ClassNotFoundException e ) { 
      System.err.println( e ); 
    }
    return newPanel; 
  }
```

möchte ich es zurückholen, die ich mit


```
meinPanel = loadStatistics();
```

aufrufe, aber nach dem Laden wird das Panel nicht mit den Objekten gefüllt . Irgendwo muss wohl noch ein Fehler sein. Aber den werde ich auch noch finden.


----------



## Gast2 (3. Jan 2012)

Und dein letzter Post beweist wie einfach man Empfehlungen ignorieren kann ... 

Anstatt es richtig zu machen wurschtelst du dich damit durch doch die GUI selber zu speichern. 

Viel Glück und Adios!


----------



## bERt0r (3. Jan 2012)

Der bug den du gepostet hast ist von 2003...
So (De)Serialisiert man ein TableModel:

```
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;


public class Serialize2 extends JFrame {

	private JPanel contentPane;
	private JTable table;
	private File tempFile;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Serialize2 frame = new Serialize2();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Serialize2() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		table = new JTable();
		table.setRowHeight(30);
		table.setModel(new DefaultTableModel(
	            new Object[][] {
	                {"Sepp",new Integer(1)},
	                { "Fritz", 2},
	                { "Karl", 3},
	                { "Georg",4},
	                { "Franz", 5},
	            },
	            new String[] {
	                "Name", "Bewertung"
	            }
	        ) {
	            /**
	             * 
	             */
	            private static final long serialVersionUID = 1L;
	            Class[] columnTypes = new Class[] {
	                Object.class, String.class
	            };
	            public Class getColumnClass(int columnIndex) {
	                return columnTypes[columnIndex];
	            }
	            private void writeObject(ObjectOutputStream out) throws IOException
	            {
	                out.writeObject(this.columnIdentifiers);
	                out.writeObject(columnTypes);
	                out.writeObject(this.dataVector);
	            }
	            
	            private void readObject(ObjectInputStream in) throws IOException
	            {
	            	
	                try{
	                    Object o=in.readObject();
	                    if(o instanceof Vector)
	                    {
	                        this.columnIdentifiers=(Vector<?>)o;
	                    }
	                    o=in.readObject();
	                    if(o instanceof Class[])
	                    {
	                        this.columnTypes=(Class[])o;
	                    }
	                    o=in.readObject();
	                    if(o instanceof Vector)
	                    {
	                        this.dataVector=(Vector<?>)o;
	                    }
	                }catch(ClassNotFoundException e)
	                {
	                    e.printStackTrace();
	                }
	 
	            }
	        });
		contentPane.add(table, BorderLayout.CENTER);
		
		JPanel panel = new JPanel();
		contentPane.add(panel, BorderLayout.SOUTH);
		
		JButton btnSerialize = new JButton("Serialize");
		btnSerialize.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) 
			{
				try {
					tempFile=File.createTempFile("Table", "tmp");
					ObjectOutputStream o=new ObjectOutputStream(new FileOutputStream(tempFile));
					table.removeEditor();
					o.writeObject(table.getModel());
					o.close();
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		panel.add(btnSerialize);
		
		JButton btnDeserialize = new JButton("Deserialize");
		btnDeserialize.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) 
			{
				if(tempFile==null)
				{
					JOptionPane.showMessageDialog((Component) e.getSource(), "Fehler","Erst serialisieren!",JOptionPane.ERROR_MESSAGE);
				}
				else
				{
				try {
					ObjectInputStream in=new ObjectInputStream(new FileInputStream(tempFile));
					TableModel tm=(TableModel)in.readObject();
					table.setModel(tm);
					table.revalidate();
				} catch (FileNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				}
			}
		});
		panel.add(btnDeserialize);
	}

}
```


----------



## André Uhres (3. Jan 2012)

Nachtfalke hat gesagt.:


> Genauso hatte ich mir das gedacht.



Welche von den drei Alternativen hattest Du dir denn gedacht?



Nachtfalke hat gesagt.:


> Inzwischen funktioniert die Serialisierung einwandfrei.



Und woher willst Du das wissen, wenn Du die Komponente nicht laden kannst?



Nachtfalke hat gesagt.:


> Irgendwo muss wohl noch ein Fehler sein. Aber den werde ich auch noch finden.



Dann viel Glück. Falls es doch nicht funktionieren sollte: es wurden Dir ja schon eine ganze Menge Ideen vorgeschlagen, die Du dann noch ausprobieren kannst .

Gruß,
André


----------



## Nachtfalke (3. Jan 2012)

bERt0r hat gesagt.:


> Der bug den du gepostet hast ist von 2003...
> So (De)Serialisiert man ein TableModel:



Fixen sich Bugs neuerdings mit der Zeit von selbst? Der Status steht immernoch auf Accepted. Folglich ist er auch in der aktuellen Java-Version noch vorhanden.


----------



## bERt0r (3. Jan 2012)

Also wenn du nicht daran interessiert bist dein Problem zu lösen, bin ich es auch nicht.


----------



## Nachtfalke (3. Jan 2012)

André Uhres hat gesagt.:


> Und woher willst Du das wissen, wenn Du die Komponente nicht laden kannst?



Das Speichern läuft ohne Fehlermeldung durch und die geschriebene Datei hat eine Größe von ca. 5,5 MB. Die Wahrscheinlichkeit, daß das Schreiben einwandfrei funktioniert hat ist damit ziemlich hoch, denke ich.


----------



## André Uhres (3. Jan 2012)

Nachtfalke hat gesagt.:


> Das Speichern läuft ohne Fehlermeldung durch und die geschriebene Datei hat eine Größe von ca. 5,5 MB. Die Wahrscheinlichkeit, daß das Schreiben einwandfrei funktioniert hat ist damit ziemlich hoch, denke ich.



Leider kannst Du nicht sehen, *was* er schreibt, da es eine binäre Datei ist. Somit kannst Du nicht wissen, ob alle relevanten Daten gespeichert werden. Zudem wird die binäre Variante (im Gegensatz zu XMLEncoder) für langfristiges Speichern nicht empfohlen, weil verschiedene Java-Versionen unterschiedliche Dateien erzeugen können, die mitunter nicht kompatibel sind. In dem Fall könnte Dein Programm nicht mehr lauffähig sein, wenn eine neue Java-Version installiert wird.

Gruß,
André


----------



## hdi (3. Jan 2012)

Sag mal, was *genau *willst du eigentlich speichern bzw laden? Du redest hier von irgendwelchen Panel-Objekten. Was soll das sein? Werd mal konkret, dann können wir dir auch sagen wie du das am besten angehen solltest. Ich befürchte du vermischt hier zwei Sachen:

1) Willst du die eigentlichen Daten speichern, die angezeigt werden, d.h. der Inhalt der Table (=Business Model)

2) Oder willst du sowas wie Spalten-Anordnung, Tabellen-Selektion oder irgendwelche eingestellten Farben usw speichern? 

Oder beides?


----------



## Nachtfalke (3. Jan 2012)

hdi hat gesagt.:


> Sag mal, was *genau *willst du eigentlich speichern bzw laden? Du redest hier von irgendwelchen Panel-Objekten. Was soll das sein? Werd mal konkret, dann können wir dir auch sagen wie du das am besten angehen solltest. Ich befürchte du vermischt hier zwei Sachen:
> 
> 1) Willst du die eigentlichen Daten speichern, die angezeigt werden, d.h. der Inhalt der Table (=Business Model)
> 
> ...



beides.


----------



## André Uhres (3. Jan 2012)

hdi hat gesagt.:


> 2) Oder willst du sowas wie Spalten-Anordnung ... speichern?



Die Spalten-Anordnung (und Spalten-Breiten) könnte man noch unter 1) aufzählen, weil JTable ein TableColumnModel hat, das man problemlos speichern kann.

Er will aber auch noch die Zeilenhöhe speichern. Das wurde bereits besprochen. 

Er scheint aber noch nicht so richtig auf die jeweiligen Vorschläge einzugehen und reagiert oft kurz angebunden, selten konkret. Er will wahrscheinlich erst mal seine eigenen Ideen soweit verwirklichen, bis er selbst sieht, dass es so nicht funktionieren kann. Jedem Tierchen, sein Pläsierchen .

Gruß,
André


----------



## hdi (3. Jan 2012)

Gut, wie du die eigentlichen Tabellen-Inhalte abspeichern kannst hat dir kappesf ja schon gesagt: Die Daten sollten nicht unmittelbarer Bestandteil der Table sein, sondern separat in deiner Anwendung vorhanden sein, und lediglich über ein TableModel auf die Tabelle gemappt werden. Deine Daten sollten also *nicht *direkt ins TableModel eingefügt oder daraus gelöscht werden. Diese Business-Daten kannst du dann erstmal völlig unabhängig von der Tabelle abspeichern. Das Laden ist dann auch unproblematisch, weil bei korrekter MVC-Implementierung der Table mitbekommt wenn neue Business-Daten da sind und diese "automtaisch anzeigt." Für das Speichern dieser Daten würd ich jetzt aber nicht unbedingt Serialisierung vornehmen. Schreib dir einfach ne eigene Methode zum speichern und laden deiner Daten.

Was nun den Zustand der GUI angeht: Was genau serialisierst du, und was meinst du mit "das Panel wird nicht mit Objekten gefüllt". Welches Panel? Ist das ein JPanel? Und was sind die Objekte?


----------



## André Uhres (3. Jan 2012)

hdi hat gesagt.:


> Die Daten sollten nicht unmittelbarer Bestandteil der Table sein, sondern separat in deiner Anwendung vorhanden sein



Das wurde in der Tat schon gesagt, jedoch ist das TableModel bereits separat und eine wirkliche Notwendigkeit für die vorgeschlagene Daten-Verdoppelung sehe ich nicht. MVC kann auch in einer Anwendung auf mehreren Ebenen agieren und außerdem muss man MVC auch nicht immer allzu stur befolgen, sondern auch danach schauen, was praktikabel ist.

Gruß,
André


----------



## hdi (3. Jan 2012)

Achso er verwendet also eine eigene Datenstruktur für seine Daten, die halt gleich schon TableModel implementiert? Klar, DefaultTableModel ist so gesehen auch separat, aber naja.. Man bleibt halt an den/einen Table gekettet. Warum du von Daten-Verdopplung sprichst versteh ich aber nicht. Es verdoppelt sich doch nix nur weil ein TableModel dazukommt.


----------



## André Uhres (3. Jan 2012)

hdi hat gesagt.:


> aber naja.. Man bleibt halt an den/einen Table gekettet.



Ich verstehe nicht, wieso man an den/einen Table gekettet bleibt, wenn das Model separat ist.



hdi hat gesagt.:


> Warum du von Daten-Verdopplung sprichst versteh ich aber nicht. Es verdoppelt sich doch nix nur weil ein TableModel dazukommt.



Sorry, dass hatte ich falsch interpretiert.

Gruß,
André


----------



## hdi (3. Jan 2012)

> Ich verstehe nicht, wieso man an den/einen Table gekettet bleibt, wenn das Model separat ist.


Aber das ist es ja nicht?! Soweit ich das jetzt verstanden hab hat der TO seine Daten direkt in einem *TableModel*. D.h. als direkten Bestandteil dieser View. TableModel heißt ja nur "Model", aber es aus Sicht von MVC gehört es nicht zum M, sondern zum V. Ein TableModel ist nun mal auf einen JTable ausgelegt, und nicht beispielsweise auf eine JList oder eine komplett UI-unabhängige Datenstruktur. Er ist einen Table gekettet, und kann zB nicht mal eben auf eine List umsteigen, denn JList kann mit einem TableModel nix anfangen. Er muss das TableModel also durch ein ListModel ersetzen. Dann wiederum ist sein TableModel futsch, also wenn die Table als zweite Komponente hinzukommen soll, gibt's wieder ein PRoblem.

Aber irgendwie müssen wir aneinander vorbeireden, denn mir ist klar dass du ganz genau weißt was ein Table- oder ListModel ist


----------



## bERt0r (3. Jan 2012)

Wie Andre schon sagte kommt das eben auf die Anwendung drauf an und man muss man nicht immer stur MVC befolgen. Wenn er seine Daten noch in einer List braucht, spricht nichts dagegen dass das TableModel auch noch das ListModel Interface implementiert.
Wenn du deine Daten z.B aus einer Datenbank ziehst, wirst du natürlich auch noch eine andere MVC Schicht haben. Sind die Daten aber sticknormale Properties oder sowas, was ausm File eingelesen wird und nur in der Table angezeigt wird, so wies hier anscheinend der Fall ist sehe ich den Nutzen von noch einer MVC Schicht nicht.


----------



## hdi (3. Jan 2012)

> Wenn er seine Daten noch in einer List braucht, spricht nichts dagegen dass das TableModel auch noch das ListModel Interface implementiert.


Nur dass es dabei nicht bleibt. Denn wenn das TableModel die Daten ändert kriegt die List nix davon mit, und andersrum. Du darfst also im TableModel auch noch Events für die List feuern, und umgekehrt. Ich meine, es geht natürlich. Aber ich würd das generell nicht machen, egal wie primitiv die Anwendung ist. Man braucht ja nur eine einzige kleine weitere Klasse und nen Listener, und schon ist das sauber getrennt, und GUI und Core sind synchronisiert, egal wo oder wie die Daten verändert werden. Wenn zB irgendein Thread im Hintergrund etwas tun soll wenn neue Daten hinzukommen oder welche gelöscht werden, dann müsstest du diese Thread ja auch direkt in deine TableModel-ListModel-Implementierung reinklatschen. Und das ist dann der Punkt wo Model und View komplett durcheinander geraten.


----------



## bERt0r (3. Jan 2012)

Events feuern musst du doch sowieso, oder setzt du dann bei jeder Änderung deines Datenmodells ein neues TableModel rein? Ich versteh jetzt nicht ganz was du meinst, vielleicht reden wir auch aneinander vorbei.
Schau wie schön einfach das funktioniert:

```
package TableListModel;

import java.util.List;
import java.util.Vector;

import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;

public class TableListModel extends DefaultTableModel implements ListModel
{
	List<ListDataListener> listenerList=new Vector<ListDataListener>();

	public TableListModel(Object[][] objects, String[] strings) {
		super(objects,strings);
	}

	@Override
	public void addListDataListener(ListDataListener l) 
	{
		listenerList.add(l);
	}

	@Override
	public Object getElementAt(int i) {
		return this.getDataVector().get(i);
	}

	@Override
	public int getSize() {
		return this.getDataVector().size();
	}

	@Override
	public void removeListDataListener(ListDataListener l) 
	{
		listenerList.remove(l);
	}
		
	@Override
	public void fireTableChanged(TableModelEvent e)
	{
		super.fireTableChanged(e);
		if(listenerList!=null)
		{
			for(ListDataListener l: listenerList)
			{
				l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, e.getFirstRow(),e.getLastRow()));
			}
		}
	}	
}
```


----------



## hdi (3. Jan 2012)

> Events feuern musst du doch sowieso, oder setzt du dann bei jeder Änderung deines Datenmodells ein neues TableModel rein?


Nein, natürlich nicht. Aber ich feuer das Event über einen UI-unabhängigen BusinessData-Listener und reagiere in den einzelnen UI-Komponenten separat. So ist jede Komponente nur für sich verantwortlich, und ich muss mich nicht darum kümmern das Ganze jetzt von dort jeweils an irgendwelche Instanzen weiterzuleiten die mich eigentlich gar nix angehen.



> Schau wie schön einfach das funktioniert:


Ja, so schön einfach funktioniert eine Read-Only List, die darüber hinaus die Einschränkung hat dass sie genau die Daten anzeigen muss die auch die Table anzeigt. Was passiert, wenn du diese beiden Views unabhängig voneinander steuern willst? So ist der nämlich in 99% der Fälle, wenn man die selben Daten in zwei verschiedenen Views hat. Wenn du die Daten nicht in der Table sondern in der List bearbeiten willst? Genau, das selbe Spiel nochmal andersrum implementieren, und in der fireListDataChanged() über deine TableDataListener drüberlaufen. Ist halt alles doppelt gemoppelt. Und das steigt exponentiell. Hast du 3 Views, hast du in JEDEM der Models den Aufwand ALLE 3 zu benachrichtigen. Und das Problem, dass UI-unabhängige Instanzen da jetzt gar nicht reinpassen hast du wohl unter den Tisch gekehrt  

Sorry, aber dieses Beispiel ist jetzt schon so eingeschränkt und mit nicht gewollten Abhängigkeiten verknüpft dass ich bei meiner Meinung festhalte. Den Code, den ich für meine externe Model-Klasse und den Listene schreibe, würdest du schon jetzt toppen wenn du die Synchronisation von Table und List auch in die andere Richtung realisieren willst.


----------



## André Uhres (4. Jan 2012)

hdi hat gesagt.:


> Ein TableModel ist nun mal auf einen JTable ausgelegt, und nicht beispielsweise auf eine JList oder eine komplett UI-unabhängige Datenstruktur.



Ob man das TableModel für eine Anwendung nutzt, wenn es passt, oder ein neues Model erfindet (das am Ende das Gleiche macht), dann sehe ich zwischen den beiden Möglichkeiten keinen wesentlichen Unterschied, außer dass vorhandener Code im ersten Fall wiederverwendet wird, was ich dann trotzdem bevorzuge .

Gruß,
André


----------



## hdi (4. Jan 2012)

> Ob man das TableModel für eine Anwendung nutzt, wenn es passt...


Aber wann passt das schon? Ich meine was ist das für eine Software, wo du deine Core-Daten spezifiziert und in diesem Schritt schon sagst "Alles dreht sich um ne JTable". Denn das ist die Aussage wenn dein Core-Model ein TableModel ist. Das kann doch keine richtige Anwendung sein, außer eine JTable-Demo von Sun.



> dann sehe ich zwischen den beiden Möglichkeiten keinen wesentlichen Unterschied, außer dass vorhandener Code im ersten Fall wiederverwendet wird, was ich dann trotzdem bevorzuge


Also ich nehm mir lieber die Zeit für eigenen Code, der dann auch genau passt, statt dass ich etwas hernehme nur weil es halt grad da ist. Ich meine schau mal im Endeffekt ist deine Aussage: "Ich habe keine Lust eine kleine Klasse mit 3-4 Methoden und einer Interface mit 3-4 Methoden zu schreiben, und lege mich aus diesem Grund darauf fest dass mein Programm im Wesentlichen irgendwas mit nem JTable zu tun hat". So wirkt das auf mich..

Naja, aber wie hast du heute schon irgendwo geschrieben: Jedem Tierchen, sein Pläsierchen  Und den letzten Satz bitte nicht falsch verstehen. Ich bin mir ziemlich sicher dass du mehr Erfahrung hast als ich und gute Software schreibst, bzw. deine Vorzeige-Software besser ist als meine. Nur ich persönlich sehe hier einfach keinen Pluspunkt, abgesehen eben davon dass du dir etwa 50 Zeilen Code sparst :bahnhof:


----------



## bERt0r (4. Jan 2012)

> Ja, so schön einfach funktioniert eine Read-Only List, die darüber hinaus die Einschränkung hat dass sie genau die Daten anzeigen muss die auch die Table anzeigt. Was passiert, wenn du diese beiden Views unabhängig voneinander steuern willst?


Nun die Standard JList ist nunmal Read only, ich weis jetzt gar nicht auf die schnelle wie man eine JList macht, in der man rumschreiben kann.
Zu zweiterem, ich dachte hier gehts darum die selben Daten in zwei Views anzuzeigen.

Ausserdem müssen sich doch bei deiner Variante alle Models bei deinem Datenmodell dann registrieren, damit die Events gefeuert werden, oder? Und wie funktioniert das, wenn das Tablemodel einen Datensatz ändert? Wo steckt der fire Aufruf an die anderen Models oder verwendest du eh nicht die Default-Model Klassen bzw. überschreibst du ihre Funktionen?


----------



## hdi (4. Jan 2012)

> Nun die Standard JList ist nunmal Read only, ich weis jetzt gar nicht auf die schnelle wie man eine JList macht, in der man rumschreiben kann.


Ja, ok der geht an dich  Ich dachte mehr an den "Bereich, in dem die JList liegt", also evtl damit verbundene Buttons usw, und weniger an die JList-Komponente selber.



> Zu zweiterem, ich dachte hier gehts darum die selben Daten in zwei Views anzuzeigen.


Es ging mir um das selbe _Model_, was aber nicht heißt dass exakt die selben Datensätze daraus angezeigt werden in der JList und im JTable. Dein Problem ist wenn zB die JList gefiltert ist. Kann sein dass ein neuer Table Eintrag in der List gar nicht auftauchen darf. Dann ist die Frage was genau du für ein ListDataEvent feuerst wenn sich was in der Table ändert. Auch diese Infos hast du dann in deiner "Gott-Klasse" die immer größer und unübersichtlicher wird.



> Ausserdem müssen sich doch bei deiner Variante alle Models bei deinem Datenmodell dann registrieren, damit die Events gefeuert werden, oder?


Genau, so mach ich das auch. Wenn ich eine neue View erstelle hab ich einen addListener() aufruf am model, wenn ich eine entferne einen removeListener()-Aufruf. Das geht ziemlich fix. Für den Listener hab ich ja nen abstrakten Datentyp.



> Und wie funktioniert das, wenn das Tablemodel einen Datensatz ändert? Wo steckt der fire Aufruf an die anderen Models oder verwendest du eh nicht die Default-Model Klassen bzw. überschreibst du ihre Funktionen?


Die einzelnen Views haben eine Referenz auf das Business-Model, und rufen dort entsprechende manipulative Methoden auf. Das Busniess-Model schickt das Event an alle registrierten Listener. Damit kriegt jede Instanz eine Änderung mit, egal wodurch diese Änderung ausgelöst wurde. Und zwar nicht nur innerhalb der GUI, sondern auch in Bezug auf tieferliegende Instanzen, wie zB irgendwelche Background Threads o.ä. Die Notifikation vom Model gelangt so auch als Callback bei der View die das ganze ausgelöst hat. D.h. ich ruf in meinem TableModel nach einem add() im Model nicht selbst irgendein fire auf mir auf, sondern lass es über ein Callback auslösen. Kann ja sein, dass das adden vom model verweigert wird und sich gar nix tut.

Also so zieh ich all meine GUI-Applikationen auf. ICh kann so sehr leicht neue Views hinzufügen oder welche löschen, und ich muss die dann nur beim Business Model an-/abmelden. Sowohl die einzelnen Views, als auch die gesamte business-Schicht sind vollständig unabhängig voneinander. Im TableModel steht nix von ListModel, im ListModel steht nix von TableModel, und im Business-Model steht nix von beidem, da hab ich nur ganz abstrakte Listener. Was das für GUI-Komponenten sind, und ob das überhaupt Komponenten vom GUI sind, das kann mir egal sein.


----------



## bERt0r (4. Jan 2012)

> Die einzelnen Views haben eine Referenz auf das Business-Model





> D.h. ich ruf in meinem TableModel nach einem add() im Model nicht selbst irgendein fire auf mir auf, sondern lass es über ein Callback auslösen.


Das heisst du schreibst dir auch jede deiner Views neu oder wie darf ich das verstehen? Irgendwie klingt das doch nach sehr viel mehr Aufwand als eine Instanz dieses Models da oben an 2 Konstruktoren zu übergeben, aber jedem das seine  Auch wenns den TO sicher nicht mehr interessiert, haben wir zumindest eine super Diskussion aus dem Thread gemacht  </spam>


----------



## hdi (4. Jan 2012)

> Das heisst du schreibst dir auch jede deiner Views neu oder wie darf ich das verstehen? Irgendwie klingt das doch nach sehr viel mehr Aufwand als eine Instanz dieses Models da oben an 2 Konstruktoren zu übergeben, aber jedem das seine


Du, ob du nun 1 Klasse hast die beide Interfaces implementiert, oder 2 Klassen die jeweils eines davon implementieren ist doch das selbe vom Aufwand her. Ja, okay, ich hab 2 Konstruktoren, du nur einen   Du darfst nicht vergessen dass meine View-Klassen im Gegensatz zu deiner Klasse ja jeweils wirklich nur noch das Mapping auf diese eine View enthalten, das ganze "Core"-Zeug vom Model ist ja in meinem Business-Model, und das hab ich nur einmal. Diese Business-Model Klasse und deren ListenerInterface sind das einzige, was ich mehr an Code hab. Aber durch die saubere Trennung spar ich mir auf Seiten der View wiederum ein paar Zeilen, zB weil ich eben nicht an verschiedensten Stellen mehrere verschiedene fire-Methoden aufrufen muss. Und ich kann eben auch vereinzelt Views löschen, das kannst du nicht so easy. Wirf jetzt mal deine Table über den Haufen: Deine ganze App reagiert nur auf TableDataEvents, die es jetzt nicht mehr gibt.. Also schön alles auf ListDataEvents umwälzen, DAS ist Aufwand ueh: 



> Auch wenns den TO sicher nicht mehr interessiert, haben wir zumindest eine super Diskussion aus dem Thread gemacht


Na wenigstens etwas  In diesem Sinne, gute Nacht!


----------



## André Uhres (4. Jan 2012)

hdi hat gesagt.:


> Aber wann passt das schon?



Daten, die in Tabellenform vorliegen, gibt es ziemlich oft. Die müssen aber nicht immer für JTable gedacht sein. Hier ist z.B. der WindController vom MVC Tutorial aus unseren FAQ, in einer erweiterten Version, für die Windmessungen von vier verschiedenen Orten:

```
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class WindController {

    private Wind wind;

    public WindController() {
        WindViewer viewer = new WindViewer(this);
        wind = new Wind();
        wind.addTableModelListener(viewer);
    }

    public void changeDirection() {
        int row = (int) (Math.random() * 4);
        Direction direction = Direction.values()[(int) (Math.random() * 4)];
        wind.setValueAt(direction, row, 1);
    }

    public void changeSpeed() {
        int row = (int) (Math.random() * 4);
        int speed = (int) (Math.random() * 100);
        wind.setValueAt(speed, row, 2);
    }

    public Wind getWind() {
        return wind;
    }

    public static void main(final String[] args) {
        Runnable gui = new Runnable() {

            @Override
            public void run() {
                new WindController();
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}

class Wind extends DefaultTableModel {

    public Wind() {
        super(0, 3);
        addRow(new Object[]{"Paris", Direction.EAST, 2});
        addRow(new Object[]{"Brüssel", Direction.EAST, 10});
        addRow(new Object[]{"Luxembourg", Direction.NORTH, 8});
        addRow(new Object[]{"Berlin", Direction.WEST, 23});
    }
}

enum Direction {

    NORTH, EAST, SOUTH, WEST
}

class WindViewer extends JFrame implements TableModelListener {

    private WindController controller;
    private JLabel id;
    private JLabel direction;
    private JLabel speed;
    private JPanel buttonPanel;
    private JPanel mainPanel;

    public WindViewer(WindController controller) {
        super("WindViewer");
        this.controller = controller;
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        init();
        setSize(400, 100);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private void init() {
        buttonPanel = new JPanel();
        mainPanel = new JPanel();
        id = new JLabel();
        direction = new JLabel();
        speed = new JLabel();
        mainPanel.add(new JLabel("Id: "));
        mainPanel.add(id);
        mainPanel.add(new JLabel("Direction: "));
        mainPanel.add(direction);
        mainPanel.add(new JLabel("Speed: "));
        mainPanel.add(speed);
        getContentPane().add(mainPanel);

        JButton button = new JButton("Change Direction");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent event) {
                controller.changeDirection();
            }
        });
        buttonPanel.add(button);

        button = new JButton("Change Speed");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent event) {
                controller.changeSpeed();
            }
        });
        buttonPanel.add(button);
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    }

    @Override
    public void tableChanged(TableModelEvent e) {
        int row = e.getFirstRow();
        Wind wind = controller.getWind();
        id.setText(wind.getValueAt(row, 0).toString());
        direction.setText(wind.getValueAt(row, 1).toString());
        speed.setText(wind.getValueAt(row, 2).toString());

    }
}
```
Gruß,
André


----------



## bERt0r (4. Jan 2012)

So schlecht geschlafen, will Klarheit 
@Hdi, deine JTable Klasse implementiert das TableModel interface oder wie? Und wieso sollte ich irgendwas umimplementieren müssen, nur weil der JTable weg ist kann mein Model immer noch Events werfen. Der JTable hört sie dann halt nicht mehr, die list aber sehr wohl.


----------



## Gast2 (4. Jan 2012)

ICh muss hdi absolut recht geben. Seine Ausführungen bezüglich der Festlegung auf einen JTable waren genau der Grund wieso ich das Business Model angesprochen hatte. Auch wenn das TableModel schon ein Model ist so legt man sich fest. 

Man kann bei Wechsel der Anzeigeart natürlich sein Model anpassen, aber ist das wirklich sinnvoll? 

Unabhängig davon geht der TO auf diese Thematik sowieso nicht ein sondern versucht einfach nach wie vor alles aus der GUI zu serialisieren.


----------



## ThreadPool (4. Jan 2012)

hdi hat gesagt.:


> [...]
> Die einzelnen Views haben eine Referenz auf das Business-Model, und rufen dort entsprechende manipulative Methoden auf.
> [...]
> Sowohl die einzelnen Views, als auch die gesamte business-Schicht sind vollständig unabhängig voneinander.[...]



Letzteres kann so nicht stimmen wenn Ersteres gilt. Sobald die View irgendeinen Typen aus der Domäne kennt und auch noch "manipulative"-Methoden darauf aufruft sind die nicht mehr unabhängig.

Übrigens durch solche Gedankengänge wer wen kennen und wer mit wem über was kommunizieren soll sind reichlich Variationen bezgl. MVC entstanden und irgendwie gibt es immer Anwendungen in denen man die eine oder andere Variation bevorzugen kann.


----------



## Gast2 (4. Jan 2012)

ThreadPool hat gesagt.:


> Übrigens durch solche Gedankengänge wer wen kennen und wer mit wem über was kommunizieren soll sind reichlich Variationen bezgl. MVC entstanden und irgendwie gibt es immer Anwendungen in denen man die eine oder andere Variation bevorzugen kann.



Stimmt, jedoch sollte in jedem Fall die Business Logik == Model nichts von der GUI wissen! Die GUI kennt ja zumindest den Controller bzw. direkt das Model. Sei es als INterface. 

Nutzt man als Controller z.B. ein Binding Framework muss ja irgendwer binden. Macht das keine eigene Klasse die sowohl View als auch Model kennt tuts meistens die GUI selbst. 

In jedem Fall kennt dann das Model aber die GUI und den Controller (in dem Fall das Framework) nicht. 

Somit ist die Geschäftslogik vollständig von der GUI getrennt, nur umgekehrt muss das nicht gelten. Ist aber imho auch nur in der einen Richtung so wichtig. Die GUI muss sich ja ohnehin an den Inhalten der Model orientieren.


----------



## Nachtfalke (4. Jan 2012)

Da der Thread nun ziemlich weit von der ursprüngliche Frage in die Programmierphilosophie weggedriftet ist, kann er von mir aus geschlossen werden. Die Vorschläge, die bezüglich des ursprünglichen Problems gefallen sind, sind alle interessant, würden in meinem Fall aber ein Redesign der Anwendung erfordern. Dafür ist mir der Aufwand zu hoch. Ich war davon ausgegangen, daß ich die angezeigten Objekte per Serialisierung relativ aufwandsarm speichern und wiederherstellen könnte. Anscheinend geht das nicht.


----------



## Gast2 (4. Jan 2012)

[OT]





> Da der Thread nun ziemlich weit von der ursprüngliche Frage in die Programmierphilosophie weggedriftet ist


Nur so am Rande, das ist keine Philosophie sondern Best Practise... 
Das du nun nicht redesignen kannst/magst versteh ich schon. Das kommt nunmal vor.[/OT]


----------



## hdi (4. Jan 2012)

> @Hdi, deine JTable Klasse implementiert das TableModel interface oder wie?


Ja klar. Das TableModel bestimmt welche Daten wie in der Table landen. Voralleme ersteres ist ein Problem, wenn deine Core-Daten das Model selbst ist. Denn was ist wenn du du zB Dateneinträge mit jeweils 5 Attributen hast, diese Table jedoch nur 3 davon anzeigen soll? Ich meine nicht alle internen Daten landen immer in der View. Du kannst in getcolumnCount nicht einfach 3 sagen, wenn dein Business Model == das TableModel, denn damit sind die anderen beiden Attribute auch für alle anderen Stellen deines Programms verschwunden, denn bei dir läuft alles über dieses TableModel. Klar, du kannst ein weiteres TableModel implementieren dass über das erste mappt. Aber genau das mach ich ja, nur dass mein "erstes TalbeModel" halt kein TableModel ist, denn das stimmt nun mal aus Sicht anderer Bereiche in meiner Applikation nicht. Sorry ich hab keine Lust TableDateEvents zu basteln, mit rows und column und so nem Quatsch, wenn ich grad auf absoluter low-level Ebene meine Daten von der Festplatte einlese. Da ruf ich lieber eine einfach addData()-Methode auf meinem Business Model auf, und das weiß intern schon selbst wie es die Daten anordnet.



> Und wieso sollte ich irgendwas umimplementieren müssen, nur weil der JTable weg ist kann mein Model immer noch Events werfen. Der JTable hört sie dann halt nicht mehr, die list aber sehr wohl.


Klar, aber wie sinnvoll ist es, irgendwelche TableDateEvents und fire-Methoden mit Angabe von irgendwelchen Row/Column-Indices zu feuern wenn du weit und breit keine Tabelle hast? Das Beispiel von André muss ich zugeben ist ja echt einigermaßen sinnvoll, aber sehr oft kann/will man sich im internen Aufbau der Daten auch nicht unbedingt auf eine tabellarische Anordnung festlegen. Und wie gesagt das heißt noch immer nicht dass man dieses Model überhaupt für ne JTable verwenden kann, wenn die Table nicht alle Infos aus dem Model anzeigen soll.



> Sobald die View irgendeinen Typen aus der Domäne kennt und auch noch "manipulative"-Methoden darauf aufruft sind die nicht mehr unabhängig.


Ok, das war dumm gesagt, du hast natürlich Recht. Komplett unabhängig kann View und Model ja nie sein, sonst gäb's keine Kommunikation. Aber die Wahrscheinlichkeit dass sich an der Struktur des Business Models was ändert ist finde ich geringer als die dass ich was an den Views ändern muss. An irgendeiner Stelle musst du immer Anpassungen vornehmen, das Business Model sollte natürlich gut überlegt sein, du kannst ja auch nochmal ein Interface für die manipulativen Methoden drüberklatschen.



> Somit ist die Geschäftslogik vollständig von der GUI getrennt, nur umgekehrt muss das nicht gelten. Ist aber imho auch nur in der einen Richtung so wichtig.


Genau, das sehe ich auch so.


----------



## bERt0r (4. Jan 2012)

Ich bin ja ganz bei dir, dass das Business Model von der GUI getrennt sein sollte, aber das trifft nunmal nur zu, wenn du überhaupt eines brauchst. Dein Businessmodel ist in Wirklichkeit nichts anderes als ein TableModel marke Eigenbau, bei dem du halt alles selber spezifizierst. Weil für jede View die du hinzufügst, musst du ja eine eigene View Komponente schreiben, die dann dein Model-Interface implementiert.
Man kann auch in einem Tablemodel die set und get-Methoden überschreiben, und die eigentliche Datenhaltung auslagern, z.B an einen DB-Manager.
Mir scheint es geht hier einfach um den Unterschied: Wiederverwendbarkeit in anderen Anwendungen vs Anpassung der Anwendung an andere Anforderungen.


----------



## hdi (4. Jan 2012)

Verstehe ich alles. Ich finde nur wenn du ein TableModel nimmst und dann anfängst alles so umzuleiten und zu overriden dass es am Ende eigentlich gar keins mehr ist, dann kannst du dir ja gleich was ganz eigenes schreiben. Und egal wie du es implementierst, der Name "TableModel" bleibt, was an sich schon eine implizite Aussage hat wie ich finde, und die sollte auch zutreffen.  Ich meine, du erbst ja auch nicht von ArrayList und machst daraus ein Set, oder? 

Aber ich glaub wir können das hier langsam zu Grabe tragen. Ich hab inzwischen verstanden was du bzw André meinen, und ich kann das in Teilen nachvollziehen. Ich persönlich versuche mir einfach aus Prinzip an gewisse Design-Regeln zu halten, auch wenn das vllt in manchen Fällen Overkill ist, was ich ja durchaus zugebe. Ist mir aber einfach lieber.


----------

