Objektabhängigkeit

Status
Nicht offen für weitere Antworten.
B

bienchen

Gast
Hallo zusammen,

also ich bin grade dabei java zu lernen und bin grade auf ein problem gestoßen, welches sich um die grundzüge der oop dreht. um das denkproblem das ich habe besser zu veranschaulichen poste ich euch mal meinen bisherigen code.
die fragen stelle ich am besten weiter unten.

Die Klasse Fenster

Code:
public class Fenster extends JFrame {
	public Fenster() {			
		
		toolkit1 = Toolkit.getDefaultToolkit();	
		Image img = toolkit1.getImage( "c://dame.jpg" );

		this.setResizable( false );
		this.setSize( 800, 600 );	
		this.setLocation( 10, 5 ); 
		this.setIconImage( img );
		this.setTitle( "Dame - Spielstart" );			
		
		battlepanel = new BattlePanel();		
		
		mainpanel = new JPanel();
		
		mainpanel.setPreferredSize( new Dimension( 250, 400 ) );
		
		shoutbox = new ShoutBox();
		infobox = new InfoBox();
		fehlerbox = new FehlerBox();
		controlbox = new ControlBox();		


		mainpanel.add( shoutbox );
		mainpanel.add( infobox);
		mainpanel.add( fehlerbox );
		mainpanel.add( controlbox );
		
		this.add( battlepanel, BorderLayout.CENTER );
		this.add( mainpanel, BorderLayout.EAST );

	}
	private Toolkit toolkit1;
	private JPanel shoutbox, controlbox, mainpanel, infobox, battlepanel, fehlerbox;
	private static final long serialVersionUID = -3853732973302441381L;
}


Die Klasse ShoutBox

Code:
public class ShoutBox extends JPanel {	
	public ShoutBox() {
		
		/* Paneleinstellungen - Farbe und Größe */
		this.setPreferredSize( new Dimension( 250, 245 ) );
		this.setToolTipText( "Spielerchat." );
				
		/* Ein Rahmen wird dem Panel hinzugefügt */
		Border etched = BorderFactory.createEtchedBorder();
		Border titled = BorderFactory.createTitledBorder( etched, " Shoutbox ");		
		this.setBorder( titled );
		
		/* TextArea für die eingegebenen und erhaltenen Nachrichten */
		textarea = new JTextArea( 8, 20 );
		textarea.setEditable( false );
		textarea.setLineWrap( true );
		scrollpane = new JScrollPane( textarea );		
		
		/* Textfeld für die Chateingaben */
		textfield = new JTextField( 20 );	
		
		/* Button Panel, der Senden und Löschen Button enthält */
		JPanel buttonpanel = new JPanel( new GridLayout( 2, 0 ) );
		buttonpanel.setPreferredSize( new Dimension( 222, 38 ) );
		
		sendbutton = new JButton( "Senden" );
		sendbutton.addActionListener( new ActionListener() {
			public void actionPerformed( ActionEvent e ) {	
				String text = textfield.getText();
				
				if( !text.equals( "" ) ) {
					textfield.setText( "" );
					textarea.append( playername + ": " + text + "\n" );
				}		
			}			
		});
		
		clearbutton = new JButton( "Löschen" );
		clearbutton.addActionListener( new ActionListener() {
			public void actionPerformed( ActionEvent e ) {			
				String text = textfield.getText();
				
				if( !text.equals( "" ) ) {
					textfield.setText( "" );
				}				
			}			
		});

		buttonpanel.add( sendbutton );
		buttonpanel.add( clearbutton );
				
		/* Komponenten dem Panel hinzufügen */
		this.add( scrollpane );
		this.add( textfield );
		this.add( buttonpanel );

	}
	private JTextField textfield;
	private JTextArea textarea;	
	private JButton sendbutton, clearbutton;
	private JScrollPane scrollpane;
	private String playername;
	private static final long serialVersionUID = 1022680599771468799L;
}

also wie bereits gepostet hab ich einen frame (erster code) und einen panel (zweiter code). ich habe jetzt mal nur ein panel gepostet. die anderen panel wie z.b. infobox, controlbox etc aus der klasse fenster haben aehnlichen inhalt. nehmen wir an ich realisiere einen button in diesem shoutbox panel, z.b. eine nachricht über das netzwerk senden und zudem soll durch das drücken des buttons eine aktion in der klasse fenster z.b. ein label wird von "nachricht nicht gesendet" in "nachricht wurde gesendet" umgewandelt werden.

mein lsg wäre ich übergebe dem panel shoutbox im konstruktor eine this referenz der klasse fenster und ändere mit z.b. parentfenster.setlabelstring( "nachricht wurde gesendet." ) das label aus der klasse fenster (parentfenster ist die referenz auf die klasse fenster). bei dieser lsg müsste ich also noch eine methode z.b. setlabelstring( ... ) in der klasse fenster erzeugen.

mein nächster gedanke wäre ich wandle die klasse shoutbox in eine inner klasse von der klasse fenster und kann somit uneingeschränkt auf die methoden und variablen zugreifen.

das wäre jetzt mein erstes problem.

eine weiteres problem ist, das ich ja nun mehrere panels in dieser klasse fenster erzeugt habe. nehmen wir an durch das drücken eines button in der klasse shoutbox (panel) soll in der klasse infobox (ist auch ein panel) ebenfalls eine aktion ausgelöst werden (z.b. ein label soll sich ändern). wie greife ich nun aus der klasse shoutbox in die klasse infobox um das label zu ändern - ich habe keine referenz.

mein lsg zu dem problem ist das ich jeder panel klasse eine methode mit .initAllpanals( JPanel infobox, JPanel fehlerbox, JPanel shoutbox ) zur verfügung stelle, über der ich dann die entsprechenden referenzen auf die entsprechenden panel klassen übergebe.
allerdings weiss ich das das totaler quatsch is. schliesslich ist das java und nich irgendeine gehackte sprache.

mein 2te lsg wäre das ich die panels in der klasse fenster anders erzeuge. also anstatt eine eigene klasse von shoutbox zu machen schreib ich dann in die klasse fenster

Code:
JPanel shoutbox = new JPanel();
shoutbox.setPreferredSize( new Dimension( 250, 245 ) );

// also quasi der inhalt von der klasse shoutbox hier hin
shoutbox.add( button );

durch die lsg hätte ich alle panels in der klasse fenster und könnte wenn ich zu einem actionlistener eine aktion hinzufüge z.b. andere jlabel ändern ohne irgendwelche referenzen zu übergeben. allerdings wüsste ich jetzt nich wie ich dann noch der klasse jpanel eigene methode hinzufügen kann.

Gibt es irgendwelche allgemeine lsg. habe viel von dem uml gehört, dass man erst planen soll und dann losprogrammiert, aber das hab ich auch noch nicht so raus. weiss einfach am anfang, wenn ich die idee habe etwas zu programmieren nicht welche klasse von welche abhängig sind und welche klasse von welcher eine referenz brauchen.

sollte man die oben beschriebenen probleme mit inneren klassen lösen oder kann man ruhig eigene klassen schreiben und mit extends JPanel erweitern oder sollte man lieber JPanel jpanel = new JPanel() nehmen und keine eigene klasse programmieren.

wann soll ich innere klassen nehmen, wann soll ich meine eigene klasse mit jpanel erweitern und wann soll ich einfach die api klasse jpanel nehmen, also jpanel = new jpanel();

ja das wäre es jetzt erstmal. hab reichlich geschrieben und ich hoffe die personen die helfen wollen bekommen kein nasenbluten wenn sie solche lsgen hoeren. ich hab mir gestern das buch analyse und design mit uml2 bestellt und versuche, wenn das buch da is sofort meine probleme zu beheben und eine entsprechende richtige lsg zu posten.

achso was ich auch noch fragen wollt ist warum die klasse unbedingt darauf drängt (muss man aber irgendwie nich) das ich eine automatisch generierte private static final long serialVersionUID = 1022680599771468799L; einfüge?

vielen dank katha
 

Marco13

Top Contributor
Erstmal kurz zur serialVersion... : Das wird AFAIK von einigen Entwicklungsumgebungen als "Warning" angekreidet, ist aber i.a. nicht notwendig. Die Warnung kann man ausschalten.

Zum Huptteil ein bißchen subjektiv-halbfundiertes Gelaber. Alles folgende stellt nur meine Meinung der. Unfug rechtfertige ich schonmal vorab mit der fortgeschrittenen Uhrzeit :cool:, und damit, dass es schon SEHR viel Code&Text war...

mein lsg wäre ich übergebe dem panel shoutbox im konstruktor eine this referenz der klasse fenster und ändere mit z.b. parentfenster.setlabelstring( "nachricht wurde gesendet." ) das label aus der klasse fenster

Das wäre grundsätzlich OK. Wenn man davon ausgeht, dass eine ShoutBox nur dann existieren kann, wenn auch ein Parentfenster existiert, ist das eigentlich legitim. Es kann aber, wenn es z.B. viele verschiedene solcher Status-Änderungen im Huptfenster gibt, u.U. dazu führen, dass man eine starke Vernetzung der Klassen bekommt, und sollte darum "mit Bedacht" eingesetzt werden

mein nächster gedanke wäre ich wandle die klasse shoutbox in eine inner klasse von der klasse fenster und kann somit uneingeschränkt auf die methoden und variablen zugreifen.

Eine Klasse zur inneren Klasse zu machen, nur um auf private Variablen zugreifen zu können, und sich davor zu drücken, sich die Gedanken zu machen, die dich zu diesem Post veranlasst haben, wäre IMHO ziemlich hackig. Konsequent weitergedacht heißt das: Man hat eine 500KB große Klasse "GUI" mit 40 inneren Klassen oder so ... :autsch:

...durch das drücken eines button in der klasse shoutbox (panel) soll in der klasse infobox (ist auch ein panel) ebenfalls eine aktion ausgelöst werden ... ich habe keine referenz.
mein lsg zu dem problem ist das ich jeder panel klasse eine methode mit .initAllpanals( JPanel infobox, JPanel fehlerbox, JPanel shoutbox ) zur verfügung stelle


Auch nicht schön. Auch wenn es in diesem Fall vielleicht so ist, dass es immer alle Panels gibt, und sich daran nie mehr was ändert, ist diese zwangsweise verdengelung ALLER Klassen grundsätzlich unschön...

mein 2te lsg wäre das ich die panels in der klasse fenster anders erzeuge. also anstatt eine eigene klasse von shoutbox zu machen schreib ich dann in die klasse fenster (...code...)

Nee - eigene Klassen sind schon OK. So eine Beziehung könnte u.U. von der hauptfenster-Klasse hergestellt werden, wenn die Klassen einander wirklich kennen müssen. Sinngemäß KÖNNTE das dann sowas sein wie
Code:
class Hauptfenster
{
    ....
    Shoutbox s = ...
    Infobox i = ...
    shoutbox.setInfoBox(i);
}

class Shoutbox
{
    private InfoBox i ... // wird von außen gesetzt

    public void actionPerformed(...)
    {
         ...
         infoBox.doSomething();
    }
}

Allerdings kann auch das u.U. wieder zu einer unschönen "Vernetzung" und gegenseitigen Abhängigkeitn führen.

Zur Erinnerung: Das ist alles nur (m)eine Meinung...

wann soll ich innere klassen nehmen, wann soll ich meine eigene klasse mit jpanel erweitern und wann soll ich einfach die api klasse jpanel nehmen

GANZ plakativ: (private) Innere Klassen sollte man nur verwenden, wenn sie untrennbar mit der einschließenden Klasse verbunden sind, und keinen bezug nach außen haben. Und die API JPanel Klasse kann man ja nicht um Funktionen erweitern - für sich ist das also nur eine "Gruppierungs-Klasse" für GUI-Komponenten. In deinen Panels sind vermutlich bestimmte Funktionalitäten "sinnvoll" zusammengefasst. Darum sollte man dafür eine eigene Klasse zu erstellen, die z.B. von JPanel erbt.

Als mögliche Antwort auf BEIDE Hauptfragen:

Wenn man die ganzen Sachen wirklich sauber trennen will, kann man mit Interfaces u.U. recht viel abstrahieren. Du hast nur "einfache" Punkte angesprochen, und ich weiß nicht, bei welchen "komplexeren" Funktionen du vor den gleichen Fragen stehen wirst. Aber vielleicht wäre (für beide Fragen - also den Informationsfluss Unterpanel->Hauptpanel als auch Unterpanel->Unterpanel) ein Listener-Modell am besten geeignet. An deinem konkreten Beispiel: Es könnte ein Interface geben, dass z.B. so aussieht
Code:
interface MessageStatusListener
{
    void setSent(boolean sent);
}
Deine Shoutbox-Klasse hat dann Methoden
Code:
addMessageStatusListener(MessageStatusListener m);
removeMessageStatusListener(MessageStatusListener m);
und verwaltet ihre MessageStatusListeners in einer ArrayList. Sobald die Nachricht gesendet wird, wird für alle MessageStatusListeners die Methode "setSent(true)" aufgerufen.
Dein Hauptfenster ist dann eine Implementierung von MessageStatusListener, und wird der Shoutbox mit obigen Methoden hinzugefügt. Die Methode setSent ist so implementiert, dass sie eben das Label mit "nachricht wurde/nicht gesendet" ensprechend beschriftet.

Der Vorteil ist, dass die ShoutBox das Hauptfenster nicht kennen muss, und auch nicht "direkt" braucht. Wenn du die Meldung in Zukunft nichtmehr im Hauptfenster anzeigen willst, sondern im InfoFenster, dann implementiert eben in Zukunft das InfoFenster das MessageStatusListener-Interface - für die ShoutBox ändert sich dadurch nichts, sie schickt nur stupide die Information über den aktuellen MessageStatus an alle Listener. Was die damit machen, ist deren Sache.

Da auch in deinem Beispiel mit der Kommunikation Shoutbox->InfoBox nur ein Label geändert werden sollte, könnte man evtl. auch ein allgemeineres Interface erstellen, z.B.
Code:
interface StatusListener
{
    void setStatus(String statusText);
}
das dann vom Hauptfenster und von der InfoBox erstellt wird. Die Shoutbox hätte dann evtl. Methoden
Code:
addMessageStatusListener(StatusListener m);
removeMessageStatusListener(StatusListener m);
addInfoStatusListener(StatusListener m);
removeInfoStatusListener(StatusListener m);
Es werden dann in beiden Fällen StatusListener verwendet, aber die einen werden benachrichtigt, wenn sich am "Message-Status" etwas ändert, und die anderen dann, wenn sich am "Info-Status" etwas ändert.

Eine weitere Möglichkeit wäre, dieses Interface etwas mächtiger zu machen:
Code:
interface StatusListener
{
    void setStatus(StatusType statusType, String statusText);
}
Dabei wäre "statusType" dann z.B. eine enum
Code:
enum StatusType { MESSAGE_STATUS, INFO_STATUS ... }
und jeder Listener achtet nur auf die Typen, die ihn interessieren.

Es kann sein, dass die letzen Vorschläge ein Abstraktions-Overkill sind, und du diese Allgemeingültigeit garnicht brauchst. Aber zumindest als Denkanstoß ist es vielleicht interessant. Es gibt viele Möglichkeiten. Welche davon für dein Ziel die beste ist, musst DU entscheiden. Und wenn du etwas in Richtung "Listener" implementieren willst, musst du selbst da noch planen, wie die Interfaces am besten aussehen sollen.... :wink:
 

bienchen

Mitglied
hallo marco13,

danke für deine antwort. hab mir das ganze nochmal angeschaut und probiert zu programmieren, wie du das mit den schnittstellen empfohlen hast. allerdings weiss ich gar nich mehr weiter. habe mal die klassen vereinfacht und mal so probiert:

Die Klasse Fenster
Code:
public class Fenster extends JFrame {
	public Fenster() {
		this.setSize( 800, 600 );
		
		Labelpanel labelpanel = new Labelpanel();
		Buttonpanel buttonpanel = new Buttonpanel();	
		
		MessageStatusListener msl = new Labelpanel();
		
		buttonpanel.addMessageStatusListener( msl );
		
		
		this.add( buttonpanel, BorderLayout.CENTER );
		this.add( labelpanel, BorderLayout.SOUTH );
	}
	private static final long serialVersionUID = 7801538395536158143L;
}

Die Klasse Buttonpanel
Code:
public class Buttonpanel extends JPanel {
	public Buttonpanel() {		
		
		al = new ArrayList<MessageStatusListener>();
		
		JButton change = new JButton( "Ändern" );
		change.addActionListener( new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				al.get(0).setSent( true );
			}			
		});
		this.add( change );
	}
	public void addMessageStatusListener( MessageStatusListener m ) {
		al.add( m );
	}
	public void removeMessageStatusListener( MessageStatusListener m ) {
		al.remove( m );
	}
	
	private static final long serialVersionUID = -6081336526492626526L;
	private ArrayList<MessageStatusListener> al;
}

Die Klasse Labelpanel
Code:
public class Labelpanel extends JPanel implements MessageStatusListener {
	public Labelpanel() {
		text = new JLabel( "Text" );		
		this.add( text );
	}
	public void setSent(boolean sent) {
		text.setText( "geändert" );
	}
	private JLabel text;
	private static final long serialVersionUID = 4586910088440047511L;
}

Das Interface
Code:
public interface MessageStatusListener {
	public void setSent( boolean sent );
}

die thematik is immernoch die selbe. ich möchte ein label (text) in der klasse labelpanel ändern. die änderung soll dann vorgenommen werden wenn ich in der klasse buttonpanel den button (change) drücke. du meintest ich könnte auf diese referenz geschichte und diesen abhängigkeiten mit dem interface ein ende setzen.

also die klasse labelpanel implementiert das interface. in der klasse buttonpanel muss ich dem button change einen actionlistener hinzufügen. die methoden um die vorhanden klassen, die benachrichtigt werden wollen habe ich auch in die klasse buttonpanel geschrieben. die werden dann von einem arraylist aufgenommen und wenn er den button drückt wird dann (jetzt nur für die klasse labelpanel die methode setSent aufgerufen). allerdings handelt es sich in dem hauptfenster nicht um die selbe referenz (msl und labelpanel), aus diesem grund wird die methode setSent nich in der referenz labelpanel aufgerufen sondern in der referenz von msl. wie muss ich das umschreiben das er das label von labelpanel ändert.

gruss katha
 

bienchen

Mitglied
hallo zusammen, also hab jetzt nochmal was versucht, bzw. nicht versucht sondern nochmal logisch durchdacht quasi...

Code:
public class FensterUe extends JFrame {
	public FensterUe() {
		this.setSize( 800, 600 );
		
		Labelpanel labelpanel = new Labelpanel();
		Buttonpanel buttonpanel = new Buttonpanel();	
		
		MessageStatusListener msl = (MessageStatusListener)labelpanel;
		
		buttonpanel.addMessageStatusListener( msl );
		
		
		this.add( buttonpanel, BorderLayout.CENTER );
		this.add( labelpanel, BorderLayout.SOUTH );
	}
	private static final long serialVersionUID = 7801538395536158143L;
}

wenn eine instanz erzeuge von Labelpanel() und es einer variablen von Labelpanel speichere, dann kann ich diese variable ohne weiteres beim this.add() hinzufügen, weil es sich ja hier um eine Klasse, die von jpanel erbt handelt. muss dann allerdings die selbe instanz nehmen und diese dann umcasten in ein messagestatuslistener damit ich sie der methode addMessageStatusListener übergeben kann. is der ansatz so jetzt besser oder würde jemand etwas anderes machen? funktionieren tut es ja...

gruss katha
 

Marco13

Top Contributor
Man braucht nicht zu casten. Ein LabelPanel IST ja ein MessageStatusListener. Es ist natürlich noch mehr - es IST auch ein JPanel, aber für die Frage, ob man es an die addMessageStatusListener-Methode übergeben kann, ist nur relevant, dass es AUCH ein MessageStatusListener ist.

Wenn du willst, kannst du jetzt z.B. auch das ButtonPanel als MessageStatusListener implementieren, und DORT dann in der setSent-Methode die Button-Aufschrift ändern oder so... Ist jetzt alles recht flexibel änderbar.

Die Tatsache, dass du direkt den ersten Schnellschuß übernommen hast, den ich ohne Kenntnis deiner Architektur vorgschlagen habe, würde nicht besonders gut durchdacht wirken, wenn ich nicht davon ausgehen würde, dass "FensterUe" für "Ueben" steht :wink: Überleg' dir, bevor du sowas einbaust, welche Interfaces für dich am sinnvollsten sind.
 

bienchen

Mitglied
ahhhhhh....! danke marco13 für deine mühe! hab das endlich auch mal gerafft. bin nämlich gerade dabei das spiel dame zu programmieren, egal ob es schon als java version gibt. das soll einfach nur ein lernreiz geben. hab da aber nochmal ne frage wobei wir gerade bei oop sind.

also wie bekannt hat das spiel dame im groben spieler, spielbrett, spielfiguren, regelwerk (soll klasse sein, die die eingaben via maus oder tastatur auf korrektheit überprüft, z.b. ob der aktuelle zug des spieler überhaupt gültig ist) und eventuell auch, wenn man es nich gegen den ki gegner spielen möchte, ein netzwerk. hab da aber jetzt noch ein paar probleme mit den beziehungen. also hab mir gedacht das...

...der spieler benutzt die spielfiguren (assoziation), denke mal das das so richtig ist. aber wie sieht es mit den anderen sachen aus z.b. netzwerkklasse, die das ganze verschickt? oder das regelwerk? und das spielbrett? meiner meinung nach benutzt der spieler auch die netzwerkklasse und das regelwerk oder nich? bei dem spielbrett hab ich gar keine ahnung. ich denke, dass das spielbrett die spielfiguren beinhaltet oder denke ich da falsch?

hat vielleicht jemand ne idee oder sieht das vielleicht anders?


gruss katha
 

Marco13

Top Contributor
Ohne zu wissen, welche Klassen du hast, ist das schwierig.

Eine Websuche nach
two player game uml architecture
http://www.google.de/search?hl=de&q=two+player+game+uml+architecture&btnG=Google-Suche&meta=
liefert z.B. diese Powerpoint-Präsentation
http://www.comp.dit.ie/bmacnamee/materials/tsdp/DesignDocumentLecture.ppt


Ich frage mich gerade, ob du die Spielfiguren wirklich als eigene Klasse implementiert hast. Es sind doch nur 2 Arten mit jeweils 2 Farben!? Jedenfalls wäre auch denkbar, dass die Spieler nur das Board kennen (dort sind ja die Spielfiguren drauf). WAS genau über das Netzwerk übertragen werden soll, ist nicht ganz klar. Vielleicht(!) ist "Netzwerk" auch nur eine implementierung der abstrakten Klasse "Player" (und würde dann auch das Board kennen). Evtl. könnte das Board auch das Regelwerk verstecken: Vielleicht (!) reicht es, wenn das Board Methoden anbietet, einen Zug zu machen - und eben irgendwo "false" liefert, falls ein Zug nicht möglich ist. Eine KI würde dann aber schon direkt das Regelwerk kennen. Hm. Überleg' dir mal ein Konzept - wenn du das dann "UML-artig"-skizzierst, und hier hochlädst, können vielleicht auch noch Luete was dazu sagen, die mehr Ahnung von Software-Engineering haben, als ich.
 

ARadauer

Top Contributor
hallo ich hab mal alles grob überflogen, kann leider jetzt keine konkrete hilfestellung schreiben, weil ich mittendrin ausgestiegen bin :? :?
aber sehr gute ansätze von marco

ich empfehl dir nur mal in google nach Observer Pattern und MVC zu suchen.
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Oben