# Wie kann ein Observer mehrere Observables beobachten?



## Silvia (2. Sep 2010)

Hallo Leute, 

ich lese schon eine Weile hier im Forum sehr interessiert mit und viele Beiträge haben mir, als Java Anfänger, auch schon über einige Hürden hinweg geholfen. Jetzt komme ich an einer Stelle nicht weiter und kann auch leider dafür keine wirkliche Lösung finden. Vielleicht könnt Ihr mir ja weiterhelfen. 

Zuerst einmal grundlegendes zum Projekt. Es soll einmal ein Tool zur Erstellung und Veränderung von Charakteren eines P&P Rollenspiels werden. Das heißt, ich werde einige verschiedene Klassen haben, die wiederum Werte und andere Daten verwalten und zB. Berechnungen ausführen. Angezeigt und verändert soll dies über eine Gui, in der die Daten je nach Eingabe des Users aktuell dargestellt werden.  Um diese doch recht umfangreichen verschiedenen Klassen und Daten Einigermassen übersichtlich und erweiterungsfähig zu halten, möchte ich das ganze mit einem MVC Observer-Pattern realisieren.

Bei dem Observer/Observable liegt nun mein Problem. Grundsätzlich bräuchte ich dafür die Möglichkeit mit meinem/-n Observer(n) mehrere Observables zu beobachten. Leider reicht mein Können und auch mein Verständnis noch nicht ganz dafür aus. Ich kann leider kein konkretes Code Beispiel finden, das mir zeigt wie die Syntax aussehen müsste und an welcher Stelle das einzubauen wäre. 

Um jetzt nicht einen riesigen Quellcode zu posten habe ich 2 Klassen mit Daten, die Gui, den Controller und die Startklasse mal in ein kleines Testprogramm gepackt und ich hoffe ihr könnt damit was anfangen. Programmiert wird mit Eclipse Helios und Java6/Swing. Bitte verhaut mich nicht gleich, ich bin noch arger Anfänger und es kann bestimmt vieles anders und besser gelöst werden. J


```
package silviTest;
import java.util.Observable;
/**
 * @author silvia
 *
 */
public class DatenModel extends Observable {

	private int mut; //Grundwert für Mut
	private int klugheit; // Grundwert Klugheit
	private int konst; // Grundwert Konstitution
	private int mr; // ist die Magieresistenz
	/**
	 * @return mutwert
	 */
	public int getMut() {
		return mut;
	}
	/**
	 * @param mut ist der Mut, der gesetzt wird
	 */
	public void setMut(int mut) {
		this.mut = mut;
		setChanged();
		notifyObservers(this);
	}
	public int getKlugheit() {
		return klugheit;
	}
	public void setKlugheit(int klugheit) {
		this.klugheit = klugheit;
		setChanged();
		notifyObservers(this);
	}
	public int getKonst() {
		return konst;
	}
	public void setKonst(int konst) {
		this.konst = konst;
		setChanged();
		notifyObservers(this);
	}
	public int getMr() {
		return mr;
	}
	public void setMr(int mr) {
		this.mr = mr;
		setChanged();
		notifyObservers(this);
	}
}
```


```
package silviTest;
import java.util.Observable;
/**
 * @author silvia
 *
 */
public class BasiswerteBerechnungen extends Observable {

	private int berechneMr;
	int mr_berechnet;
	/**
	 * @param mut
	 * @param klugheit
	 * @param konst
	 * @return mr
	 */
	public static int berechneMR(int mut, int klugheit, int konst) {
		int mr_berechnet = Math.round((mut + klugheit + konst) / 5);
		return mr_berechnet;
	}
	public void changeMr() {
		setBerechneMr(berechneMr);
		setChanged();
		notifyObservers(this); // eine Problemzone, ist das hier so richtig ?
	}
	public void setBerechneMr(int berechneMr) {
		this.berechneMr = berechneMr;
	}
	public int getBerechneMr() {
		return berechneMr;
	}
}
```


```
package silviTest;
import java.util.Observable;
import java.util.Observer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
 * @author silvia
 *
 */
public class TestController implements Observer {

	private DatenModel daten;
	private GraphischeOberflaeche fenster;
	private BasiswerteBerechnungen basis;
	/**
	 * initialisierung der beobachteten Klassen und Felder/Daten
	 * anhängen des observers und der Methoden,Listener
	 */
	public TestController() {
		daten = new DatenModel();
		fenster = new GraphischeOberflaeche();
		// basis = new BasiswerteBerechnungen(); // wäre das hier so richtig?
		daten.addObserver(this);
		//basis.addObserver(this);

		fenster.getMutSpinner().addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {			
				daten.setMut(((Integer) fenster.mutFeld.getValue()).intValue());
				basis.changeMr();
			}
		});
		fenster.getKlSpinner().addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {				
				daten.setKlugheit(((Integer) fenster.klugheitFeld.getValue()).intValue());
				basis.changeMr();
			}
		});
		fenster.getKonstSpinner().addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				daten.setKonst(((Integer) fenster.konstitutionFeld.getValue()).intValue());
				basis.changeMr();
			}
		});
	}
	@Override
	public void update(Observable o, Object arg1) {
		
			DatenModel model = (DatenModel) arg1;
BasiswerteBerechnungen basismodel = (BasiswerteBerechnungen) arg1; // hier bekomme ich die Fehler Exception: Kann nicht auf Basiswerte casten

			fenster.mutLbl.setText(String.valueOf(model.getMut()));
			fenster.klugheitLbl.setText(String.valueOf(model.getKlugheit()));
			fenster.konstLbl.setText(String.valueOf(model.getKonst()));

		fenster.mrLbl.setText(String.valueOf(basismodel.getBerechneMr()));
		}
}
```


```
package silviTest;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
/**
 * @author silvia
 *
 */
public class GraphischeOberflaeche extends JFrame implements FocusListener {
// Label zur Anzeige und Kontrolle der Daten,Berechnungen
	JLabel mutLbl = new JLabel("");
	JLabel klugheitLbl = new JLabel("");
	JLabel konstLbl = new JLabel("");
	JLabel mrLbl = new JLabel("");
	JSpinner mutFeld = new JSpinner(new SpinnerNumberModel(8, 0, 21, 1));
	JSpinner klugheitFeld = new JSpinner(new SpinnerNumberModel(8, 0, 21, 1));
	JSpinner konstitutionFeld = new JSpinner(new SpinnerNumberModel(8, 0, 21, 1));

	GridBagConstraints gbc = new GridBagConstraints();
	/**
	 * setzen der Fensterparameter
	 */
	public GraphischeOberflaeche() {

		super("Controller -> MVC Test");
		setSize(1024, 600);
		setVisible(true);

		setLayout(new GridBagLayout());
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.gridwidth = 1;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.insets = new Insets(3, 1, 3, 1);
		setBackground(Color.yellow);

		labelHinzufuegen(mutLbl, 0, 0);
		labelHinzufuegen(klugheitLbl, 0, 1);
		labelHinzufuegen(konstLbl, 0, 2);
		labelHinzufuegen(mrLbl, 3, 2);

		spinnerHinzufuegen(mutFeld, 2, 0, "Mut");
		spinnerHinzufuegen(klugheitFeld, 2, 1, "KLugheit");
		spinnerHinzufuegen(konstitutionFeld, 2, 2, "Konstitution");

		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}
	/**
	 * @param label
	 * @param x
	 * @param y
	 */
	public void labelHinzufuegen(JLabel label, int x, int y) {
		gbc.gridx = x;
		gbc.gridy = y;
		add(label, gbc);
	}
	/**
	 * @param spinner
	 * @param x
	 * @param y
	 * @param tooltip
	 */
	public void spinnerHinzufuegen(JSpinner spinner, int x, int y, String tooltip) {
		gbc.gridx = x;
		gbc.gridy = y;
		add(spinner, gbc);
		spinner.setToolTipText(tooltip);
		((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().addFocusListener(this);
	}
	/**
	 * @return mut
	 */
	public JSpinner getMutSpinner() {
		return mutFeld;
	}
	/**
	 * @return klugheit
	 */
	public JSpinner getKlSpinner() {
		return klugheitFeld;
	}
	/**
	 * @return konst
	 */
	public JSpinner getKonstSpinner() {
		return konstitutionFeld;
	}
	@Override
	public void focusGained(FocusEvent e) {
		if (e.getSource() instanceof JTextComponent) {
			final JTextComponent textComponent = ((JTextComponent) e.getSource());
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					textComponent.selectAll();
				}
			});
		}
	}
	@Override
	public void focusLost(FocusEvent e) {

	}
}
```


```
package silviTest;
import javax.swing.SwingUtilities;
/**
 * @author silvia
 *
 */
public class Main {

	public static void main(String[] args) {
		Runnable gui = new Runnable() {
			@Override
			public void run() {
				new TestController();
			}
		};
		SwingUtilities.invokeLater(gui);
	}
}
```

Anregungen, Kritik, Tipps und Hilfe sind jederzeit willkommen. Vielen Dank schon mal im vorraus an euch.
Silvia


----------



## Marco13 (2. Sep 2010)

Hab' jetzt nicht alles im Detail nachvollzogen, aber um den _Typ_ zu erkennen gäbe es grundsätzlich die Möglichkeit, sowas zu machen wie

```
public void update(Observable o, Object arg1) 
{
    if (o instanceof DatenModel)
    {
        DatenModel model = (DatenModel)o;
        ...
    } 
    else if (o instanceof BasiswerteBerechnungen)
    {
        BasiswerteBerechnungen b = (BasiswerteBerechnungen)o;
        ...
    }
}
```

Ich würde dir aber empfehlen, dir deine eigenen Klassen dafür zu machen. Die Observer/Observable Klassen aus java.util sind nur eine mögliche Ausprägung dieses Musters. Sie sind zwar sehr allgemein, aber für die meisten praktischen Zwecke "ZU" allgemein (im negativen Sinne von "ZU unspezifisch" - es tritt dann eben genau das Problem auf, dass "update" aufgerufen wird, und man erstmal nicht weiß von wem und warum). 

Für Anwendungen, wo man sein eigenes Datenmodell entwirft, sollte man IMHO auch einen eigenen Observer/Observable-Mechanismus implementieren, genaugenommen in Form von Listenern (und ggf. Events). Das allgemeine Muster könnte man grob so andeuten

```
interface Model 
{
    void setSomething(String something);
    void setSomethingElse(int somethingElse);

    void addModelListener(ModelListener modelListener);
    void removeModelListener(ModelListener modelListener);
}

// Falls es mehrere Modelle gibt, kann es praktisch sein, ein "AbstractModel" zu haben,
// wo die Listener-Verwaltung schon implementiert ist, und die anderen Methoden aus
// dem Model-Interface noch "abstract" sind:
class AbstractModel implements Model
{
    private List<ModelListener> listeners = ...

    public void addModelListener(ModelListener modelListener) { ... }
    public void removeModelListener(ModelListener modelListener) { ... }

    protected void notifyAllListenersThatSomethingChanged() { ... }
    protected void notifyAllListenersThatSomethingElseChanged() { ... }
}

// Die eigentliche Implementierung. Falls man kein AbstractModel
// hat, würden die oben genannten Methoden hier drin stehen
class DefaultModel extends AbstractModel implements Model
{
    private String s = null;

    public void setSomething(String s)
    {
        this.s = s;
        notifyAllListenersThatSomethingChanged();
    }

    ....
}


// Der Listener enthält Methoden, die in 
// 'notifyAllListenersThatSomethingChanged' aufgerufen
// werden, wenn sich am Modell etwas ändert
interface ModelListener
{
    void somethingChanged(ModelEvent event);
    void somethingElseChanged(ModelEvent event);
}

// Beschreibt eine Änderung am Modell genauer. ModelEvents
// werden in 'notifyAllListenersThatSomethingChanged' erstellt
// und an alle Listener weitergereicht
class ModelEvent { ... }
```

Ggf. kann man die Events auch weglassen, und den Listener einfach schreiben als

```
interface ModelListener
{
    void somethingChanged(String oldValue, String newValue);
}
```
oder so.


----------



## Silvia (3. Sep 2010)

Hallo,

erst einmal vielen lieben Dank Marco, für deine schnelle Hilfe. 

Ich habe die vorgeschlagenene Typübergabe in die Update-Methode eingebaut und das funktioniert auch jetzt. 


```
public void update(Observable o, Object arg1) {
		// TODO Auto-generated method stub
		if (o instanceof DatenModel) {
			DatenModel model = (DatenModel) o;
			fenster.mutLbl.setText(String.valueOf(model.getMut()));
			fenster.klugheitLbl.setText(String.valueOf(model.getKlugheit()));
			fenster.konstLbl.setText(String.valueOf(model.getKonst()));
		} else if (o instanceof BasiswerteBerechnungen) {
			BasiswerteBerechnungen basismodel = (BasiswerteBerechnungen) o;

			fenster.mrLbl.setText(String.valueOf(basismodel.getBerechneMr()));
		}
	}
```
Leider tritt dadurch ein anderes Problem auf. Das Programm gibt mir zwar die Werte aus der Datenklasse aus, jedoch nicht den berechneten Wert aus der Basisberechnungen Klasse. Es sieht so aus, als wenn der else if zweig nicht bearbeitet wird. Irgendwo ist da ein Fehler drin, den ich (noch) nicht finde. Eine Exception bekomme ich nicht, der Wert wird einfach nur nicht berechnet bzw. nicht angezeigt.

Zu deinem Vorschlag mit dem eigenen Observer-Handling. Den Vorschlag finde ich sehr gut. Nur ist das für mich noch eine Stufe zu 'hoch'. Es kommt aber mit Sicherheit auf meine ToDo-Liste.

Ich lerne Java als Autodidakt und arbeite mich durch die einzelnen Programmpunkte nach dem Prinzip: erst mal muss es funktionieren, ich muss verstehen warum und dann vielleicht nach eleganteren/ besseren Lösungen suchen.  Die Basics erarbeite ich mir dann immer zu dem aktuellen Teil. Das ist mit Sicherheit nicht die richtige oder beste Art Java zu lernen, aber so lerne ich persönlich am besten. 
Daher würde mir vorerst eine funktionierende Lösung reichen, sollte das überhaupt mit dem allgemeinen Observer-Pattern möglich sein.

mit vielen Grüßen
Silvia
PS. Anregungen, Kritik, Tipps und Hilfe sind jederzeit willkommen. Vielen Dank schon mal im vorraus an euch.


----------



## Marco13 (3. Sep 2010)

```
fenster = new GraphischeOberflaeche();
        // basis = new BasiswerteBerechnungen(); // wäre das hier so richtig?
        daten.addObserver(this);
        //basis.addObserver(this);
```
Wie sieht der Teil denn im Moment aus?


----------



## FArt (3. Sep 2010)

> Ich lerne Java als Autodidakt und arbeite mich durch die einzelnen Programmpunkte nach dem Prinzip: erst mal muss es funktionieren, ich muss verstehen warum und dann vielleicht nach eleganteren/ besseren Lösungen suchen.  Die Basics erarbeite ich mir dann immer zu dem aktuellen Teil. Das ist mit Sicherheit nicht die richtige oder beste Art Java zu lernen, aber so lerne ich persönlich am besten.
> Daher würde mir vorerst eine funktionierende Lösung reichen, sollte das überhaupt mit dem allgemeinen Observer-Pattern möglich sein.



Keine direkte Antwort:

Autodidakten haben es manchmal schwerer, lernen aber oft mehr.
Das Pattern ist ein Pattern, also nur ein Muster. Dazu gibt es verschiedenste Implementationen, abhängig von den Bedürfnissen. Ob für deinen Anwendungsfall ein generischer Observer eine gute Lösung ist kann ich erst mal nicht sicher beantworten. Nur ein Tipp: freunde dich mit dem Debugger an und versuche deine Fehlerfälle durchzusteppen... das Verständnis für den Code wird unheimlich wachsen.

... und ausreichend (massig) Logging einbauen am Anfang...


----------



## Silvia (3. Sep 2010)

Hallo Marco,

hier am besten den gesamten TestController, so wie er im moment ist.


```
package silviTest;
import java.util.Observable;
import java.util.Observer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
 * @author silvia
 *
 */
public class TestController implements Observer {
 
    private DatenModel daten;
    private GraphischeOberflaeche fenster;
    private BasiswerteBerechnungen basis;
    /**
     * initialisierung der beobachteten Klassen und Felder/Daten
     * anhängen des observers und der Methoden,Listener
     */
    public TestController() {
        daten = new DatenModel();
        fenster = new GraphischeOberflaeche();
        basis = new BasiswerteBerechnungen(); // hier 
        daten.addObserver(this);
        basis.addObserver(this); // und hier
 
        fenster.getMutSpinner().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {           
                daten.setMut(((Integer) fenster.mutFeld.getValue()).intValue());
                basis.changeMr();
            }
        });
        fenster.getKlSpinner().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {               
                daten.setKlugheit(((Integer) fenster.klugheitFeld.getValue()).intValue());
                basis.changeMr();
            }
        });
        fenster.getKonstSpinner().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                daten.setKonst(((Integer) fenster.konstitutionFeld.getValue()).intValue());
                basis.changeMr();
            }
        });
    }
	@Override
	public void update(Observable o, Object arg1) {
		if (o instanceof DatenModel) {
			DatenModel model = (DatenModel) o;
			fenster.mutLbl.setText(String.valueOf(model.getMut()));
			fenster.klugheitLbl.setText(String.valueOf(model.getKlugheit()));
			fenster.konstLbl.setText(String.valueOf(model.getKonst()));
		} else if (o instanceof BasiswerteBerechnungen) {
			BasiswerteBerechnungen basismodel = (BasiswerteBerechnungen) o;

			fenster.mrLbl.setText(String.valueOf(basismodel.getBerechneMr()));
		}
	}
}
```

ich danke dir für deine Mühe und Engelsgeduld 

viele Grüße Silvia

EDIT: vielen Dank FArt für die Anregung, werde mich gleich mal mit dem Debugger von Eclipse genauer beschäftigen, das ist ein guter Tipp. Manchmal sieht man den Wald vor lauter Bäumen nicht.

viele Grüße Silvia


----------



## Marco13 (3. Sep 2010)

Ja, der FUrz ( :bae: ) hat schon recht: So ein paar Ausgaben wie

```
@Override
    public void update(Observable o, Object arg1) {

        System.out.println("Update "+o+" arg "+arg1); //------------------------ !!!

        if (o instanceof DatenModel) {
            DatenModel model = (DatenModel) o;
            fenster.mutLbl.setText(String.valueOf(model.getMut()));
            fenster.klugheitLbl.setText(String.valueOf(model.getKlugheit()));
            fenster.konstLbl.setText(String.valueOf(model.getKonst()));
        } else if (o instanceof BasiswerteBerechnungen) {

            BasiswerteBerechnungen basismodel = (BasiswerteBerechnungen) o;

            System.out.println("*wink*");  //------------------------ !!!

            fenster.mrLbl.setText("WURDE GEÄNDERT "+String.valueOf(basismodel.getBerechneMr()));
        }
    }
```
zeigen schnell, dass das zwar ausgeführt wird, aber "getBerechneMr" immer 0 zurückgibt. Und wenn man sich daraufhin (!) die BasiswerteBerechnungen mal genauer ansieht, stellt man fest, das... die so irgendwie keinen Sinn macht. Die Methode "berechneMR" wird nie aufgerufen (und sollte nicht static sein!!!), und die Variable "berechneMr" erhält nie einen anderen Wert als 0.

Du solltest dir überlegen, wie die Struktur und die Abhängigkeiten dieser Klassen zueinander sein sollten - also speziell welchen Zweck "BasiswerteBerechnungen" hat. Welchen _Zustand_ kann diese Klasse haben? Eigentlich ist sie (im Moment!) doch nur ein "Umrechner" - d.h. sie braucht im Moment keine eigenen Variablen, sondern liefert nur Ergebnisse von Berechnungen mit Werten aus dem eigentlichen DatenModel. D.h. im Moment KÖNNTE das ganze auch so sein

```
class BasiswerteBerechnungen // NICHT Observable
{
    private DatenModel daten;
    public BasiswerteBerechnungen(DatenModel daten)
    { 
        this.daten = daten;
    }
    public int getBerechneMR() {
        Math.round((daten.getMut() + daten.getKlugheit() + daten.getKonst) / 5);
    } 
}
```
(dann würde es sich aber nicht wirklich "lohnen", dafür eine eigene Klasse zu erstellen  )

Aber FALLS diese Klasse irgendwann noch erweitert wird, und einen eigenen Zustand hat, der NICHT vollständig durch das Datenmodell bestimmt ist, würde man da vielleicht was anderes machen....

Also: Erstmal das hier ausfüllen:

```
/**
 * Diese Klasse ist ... 
 * Diese Klasse macht...
 * ...
 * @author silvia
 */
class BasiswerteBerechnungen extends Observable {
...
```


Vielleicht noch ein bißchen ausholen: Wenn man ein Datenmodell beschreiben will, hat es Vorteile, diese in Form von Interfaces zu beschreiben. Bei der Planung/Erstellung hat es den Vorteil, dass man sich wirklich darauf konzentiert, WAS eine Klasse repräsentieren soll, und WAS die Klasse machen soll. Damit ist man quasi "gezwungen", sich auf's wesentliche zu konzentieren, nämlich die Methoden, die es irgendwann (später) mal geben wird.

```
interface Spieler // aka "DatenModell"
{
    int getMut();
    int getKlugheit();
    int getKonstitution();
    int getMagieResistenz();

    // set-Methoden? Vielleicht, vielleicht auch nicht... 
}
```
(Abgesehen davon, dass man dann schon grob sieht, ob alles funktioniert - d.h. ob compilierfehler auftreten). 

Der viel wichtigere Vorteil ist aber, dass man später damit viel flexibler ist...

```
class Krieger implements Spieler
{
    private boolean besitztSchutzschild;
    private int mut;
    private int magieResistenz;
    ...
    public int getMut() 
    {
        if (besitztSchutzschild) return mut+10;
        else return mut;
    }
    public int getMagieResistenz()
    {
        return magieResistenz;
    }
    ...
}
```


```
class Magier implements Spieler
{
    private boolean besitztZauberstab;
    private int mut;
    private int magieResistenz;
    ...
    public int getMut() 
    {
        return mut;
    }
    public int getMagieResistenz()
    {
        if (besitztZauberstab) return magieResistenz*2;
        else return magieResistenz;
    }
    ...
```
(nur ein Beispiel, zur Verdeutlichung - in Wirklichkeit müßte man sich da noch mehr dazu überlegen)


----------



## Silvia (3. Sep 2010)

Hallo,

erst einmal Danke für die Denkanstöße und Tipps. Ich werde sie mir auf jeden Fall zu Herzen nehmen und umsetzen.

Vielleicht sollte ich die grobe Funktion meines Projekts kurz mal zusammenfassen, denn die Klassen die wir hier besprechen sind nur ein Bruchteil des ganzen endgültigen Programms.

Kurz beschrieben soll der User mittels einer Gui einen Spielcharakter entwerfen und durch beim P&P Spiel erworbene Punkte in neue Fähigkeiten investieren oder schon vorhandene Fähigkeiten ausbauen können. Dies geschieht nach einem im Spielsystem vorgegebenen Regelwerk.

Die Grundlage sind zb. die Grundwerte eines Charakters, die Herkunft, Beruf usw. 

```
/**
 * @author silvia
 * 
 * Diese Klasse generiert die vom Regelwerk vorgebenen Minimalen Grundwerte eines Charakters mit der
 * Standarteinstellung von 8 und können über die Gui direkt und später durch die Auswahl von
 * Berufen, Herkunft, Spezialisierungen ect. zusätzlich verändert werden.//noch zu implementieren
 * 
 * Vorgegebene Werte sind Mut, Klugheit, Intuition, Charisma, Fingerfertigkeit, Gewandheit, Konstitution,
 * Körperkraft, Sozialstatus // fehlende nachtragen
 * aus diesen Werten berechnen sich weitere Werte wie Magieresistenz, Lebenspunkte, Talente ect. * 
 */
public class DatenModel extends Observable {

	// Zu Testzwecken auf Werte beschränkt die der Berechnung der Magieresistenz zugrunde liegen
	// Mut, Klugheit, Konstitution
```

Das ist nur ein kleiner Bereich dessen, was mein Projekt in der endgültigen Fassung beinhalten soll. Da ich wie gesagt Autodidaktisch lerne, habe ich das ganze in kleine Häppchen unterteilt und mir erst einmal nur einen Teilaspekt vorgenommen. Dieser soll allerdings, wie du schon vermutet hast, später auf weitere Klassen, Berechnungen usw. erweiterbar sein. Daher ist die Klasse BasisBerechnungen im moment wirklich nur ein Umrechner, soll später jedoch durch Modifiaktoren aus einer anderen Klasse noch zusätzlich Änderungen an den Werten vornehmen können.

```
/**
 * @author silvia
 * 
 * Diese Klasse ist für die Berechnungen von Werten aus dem Datenmodel und anderen Klassen //noch zu erstellen
 * zuständig und erstellt neue Werte die in der Gui angezeigt werden.
 * 
 *  Diese Klasse berechnet aus den Datenmodelwerten:
 *  Lebenspunkte, Ausdauer, Astralenergie, Magieresistenz, Kampf-Basiswerte ect. //fehlende Nachtragen
 *  
 *  // noch zu implementieren
 *  Diese Klasse soll aus den Klassen für Berufe, Herkunft ect. die Modifikatoren zur jeweiligen 
 *  Berechnung übernehmen und diese mit den Grundberechnung verarbeiten und den neuen Wert an die
 *  Gui übergeben.  *  
 */
public class BasiswerteBerechnungen extends Observable {

	// vorläufig und zu Testzwecken auf Berechnung der Magieresistenz aus dem Datenmodel beschränkt 
	// ohne weitere Klassen und Modifikatoren
	// Anzeige der berechnete Magieresistenz in der Gui für Test in Label

	private int berechneMr;
	int mr_berechnet;

	/**
	 * diese Methode soll die übergebenen Werte berechnen und bei Änderung der Daten in der
	 * Gui diese neu berechnen und dem Controller bescheid geben das und was sich geändert hat
	 * 
	 * @param mut
	 * @param klugheit
	 * @param konst
	 * @return die berechnete magieresistenz
	 */
```

Das ergibt dann mit Abschluß der, nennen wir es ‚Generierungsphase’, die endgültigen Startwerte eines Spielcharakters.

Später soll, unter anderem, der Charakter unter seinem Namen gespeichert, gedruckt, geladen, weiterbearbeitet werden können.

Ich habe das ganze in den Grundzügen bereits mittels einer einfachen Gui, Grunddaten, BerechnungenKlasse und eingebauten Listenern soweit hinbekommen. Der Code ist dadurch aber sehr unübersichtlich und nicht gerade Wartungs- und/oder Erweiterungsfreundlich. Daher die Idee der Trennung von Gui und Daten sowie die Nutzung von Observern. Möglicherweise bin ich aber mit diesem Ansatz auch ziemlich auf dem Holzweg.

Jedenfalls Danke noch mal für deine Hilfe und ich lese mich gerade in die Verwendung von Interfaces ein. Weitere Anregungen, Tipps ect. werden wie immer gern entgegen genommen.

mit vielen Grüßen Silvia


----------



## maki (3. Sep 2010)

Meine Anregung: Schon mal den PropertyChangeSupport vom JavaBeans Standard angesehen?


----------



## Marco13 (3. Sep 2010)

Ja, die Frage http://www.java-forum.org/allgemein...rwendet-man-propertychangedevents-eigene.html hatte ich ja auch mal gestellt... Ich tendiere eher zu eigenen, weil das unspezifische Herumreichen von Strings imho fehleranfälliger und unübersichtlicher ist...

Zum "BasiswerteBerechnungen": Dem Namen nach ist diese Klasse ja für Berechnungen zuständig, und hat erstmal eigentlich keinen eigenen Zustand. Aber wenn sie, wie angedeutet, irgendwann mal einen Zustand haben soll, der von den Eigenschaften des DatenModells (Spielers) abhängt, dann müßte sie ja selbst ein Observer des Datenmodells sein - andernfalls kann der Zustand inkonsistent werden. Und spätestens, wenn eine Klasse gleichzeitig Observer und Observable ist, wird's unübersichtlich. Ein bißchen "Vertrauen" darauf, dass das ganze am Ende funktioniert, ist wohl nicht verkehrt (nicht mit "Optimismus" zu verwechseln  ). Wenn man sich erstmal die ganzen Interfaces aufschreibt, und sich klar macht, wer wie mit wem zusammenhängt, wird die Struktur IMHO klarer - das entspricht von der Modellierungs-Abstraktionsstufe her ziemlich genau UML, aber ist schneller zu ändern und mit einer modernen IDE auch schnell nachzuvollziehen.


----------

