Labyrinth über MazeGameServer visualisieren

K

kneitzel

Gast
Eine Liste zur Hand hättest, wie erstellt man denn dMn eine Liste von baittypes die man zur Hand hat?
Das ist einfache Umgangssprache.

Wenn du das Field erzeugt, dann musst du bei dem von dir vorgeschlagenen Konstruktor auch eine Liste mit übergeben.

Nun schau dir die Anforderungen an:
Wie bekommst du welche Informationen vom Server? Was bekommst du, wenn du nach dem Maze fragst? Was kannst du mit diesen Informationen machen?
 

jono

Top Contributor
Wenn du das Field erzeugt, dann musst du bei dem von dir vorgeschlagenen Konstruktor auch eine Liste mit übergeben.
Ich habe dem Konstruktor von mir doch die Liste übergeben?

Nun schau dir die Anforderungen an:
Wie bekommst du welche Informationen vom Server? Was bekommst du, wenn du nach dem Maze fragst? Was kannst du mit diesen Informationen machen?
Informationen vom Server bekomme ich, in dem ich Nachrichten an diesen sende. Wenn ich nach dem Maze frage, bekomme ich eine gesamte Spielfeldbeschreibung. Der Server sendet außerdem Informationen über die sichtbaren Objekte, über die Spieler, ihre Positions und Scores. Und der Server überträgt die Position eines Objekts. Diese Informationen kann ich in meinen Klassen aufnehmen??
 

jono

Top Contributor
Ich weiß nicht worauf du hinaus willst, was soll ich mit diesen Informationen machen können. Ich bekomme sie geschickt, sie müssen natürlich in das UI übertragen werden den CommandHandler einem Befehl geben welcher dann GameStatusModel aktualisiert.
 

Plauzi92

Aktives Mitglied
Nun schau dir die Anforderungen an:
Wie bekommst du welche Informationen vom Server? Was bekommst du, wenn du nach dem Maze fragst? Was kannst du mit diesen Informationen machen?
Ich weiß nicht worauf du hinaus willst, was soll ich mit diesen Informationen machen können. Ich bekomme sie geschickt, sie müssen natürlich in das UI übertragen werden den CommandHandler einem Befehl geben welcher dann GameStatusModel aktualisiert.


Schau mal in den Exam-Task Seite 16. Unter "Example" kommt die Serverantwort nach dem du "MAZ?" gesendet hast.
Dieses Rechteck aus Hashtags und Punkten ist das Labyrinth. Unter dem Punkt "Description" steht welches Symbol was darstellen soll. Dementsprechend setzt du für Hashtags eine Mauer und für Punkte eben nicht.
Wenn ich deinen Code richtig verstehe willst du für alle Felder ein neues "Field" erzeugen. Dann setzt du beim erzeugen deinen FieldType entsprechend entweder auf PATH oder auf WALL.
Ich persönlich würde einfach Instanzen vom Typ Wall (oder wie auch immer du es nennen willst) erzeugen die die entsprechende Position erhalten. Dann kannst du beim setzen der Baits trotzdem noch testen ob sich da eine Wand befindet oder nicht.
Ich hab nicht so ganz verstanden was du mit FieldTyp erreichen willst. Das einzige was den Typ WALL von PATH unterscheidet ist, ob da ein Spieler/Bait
sein kann oder nicht. Das heißt du überprüfst beim Setzen entweder "Ist das eine Mauer? -> Nicht setzen" oder "Ist das ein Weg? -> Setzen". (Oder die Negationen hiervon)
Je nachdem auf welchen Typ du prüfst, ist der andere in meinen Augen unnötig.
 

mrBrown

Super-Moderator
Mitarbeiter
Ich hab nicht so ganz verstanden was du mit FieldTyp erreichen willst. Das einzige was den Typ WALL von PATH unterscheidet ist, ob da ein Spieler/Bait
sein kann oder nicht. Das heißt du überprüfst beim Setzen entweder "Ist das eine Mauer? -> Nicht setzen" oder "Ist das ein Weg? -> Setzen". (Oder die Negationen hiervon)
Je nachdem auf welchen Typ du prüfst, ist der andere in meinen Augen unnötig.

Der FieldTyp dient doch genau dazu, damit sind die Abfragen "Ist das eine Mauer" und "Ist das ein Weg?" möglich.
 

jono

Top Contributor
Dann setzt du beim erzeugen deinen FieldType entsprechend entweder auf PATH oder auf WALL.
Ich persönlich würde einfach Instanzen vom Typ Wall (oder wie auch immer du es nennen willst) erzeugen die die entsprechende Position erhalten. Dann kannst du beim setzen der Baits trotzdem noch testen ob sich da eine Wand befindet oder nicht.
Habe doch schon eine Instanz vom FieldType PATH erzeugt. Die Frage war eben ,wie ich die anderen 2 noch erzeuge.
Ich brauche doch gar nicht zu prüfen, ob da eine Wand ist, denn Donald ein weg da ist, wird das bait doch gesetzt???
Wäre echt sehr dankbar wenn mich winter aufklärt,Weil ich grade echt nicht mehr weiß, wie ich writer vorgehen soll bzw. warum ich jetzt aufeinmal ein Konstruktor erzeugen soll
 

Plauzi92

Aktives Mitglied
Der FieldTyp dient doch genau dazu, damit sind die Abfragen "Ist das eine Mauer" und "Ist das ein Weg?" möglich.
Ist an der Position (X/Y) ein Objekt Field mit dem Typ "WALL"? -> Ja? -> Nicht setzen
Ist an der Position (X/Y) ein Objekt Wall? ->Ja? -> Nicht setzen

Macht doch absolut keinen Unterschied, nur dass man sich bei letzterer Variante den FieldType spart.


Wäre echt sehr dankbar wenn mich winter aufklärt,Weil ich grade echt nicht mehr weiß, wie ich writer vorgehen soll bzw. warum ich jetzt aufeinmal ein Konstruktor erzeugen soll

Den Konstruktor bräuchtest du doch auch ohne FieldType damit du der Instanz vom Typ Field die Position direkt mitgeben kannst.
 

jono

Top Contributor
Den Konstruktor bräuchtest du doch auch ohne FieldType damit du der Instanz vom Typ Field die Position direkt mitgeben kannst.
Was willst du Mir damit sagen, warum brauche ich eine Instanz vom see Klasse Field oder was meinst du mit "Typ Field"? Und wie Gebe ich jetzt genau einem Feld die Position mit?
Wäre echt nett wenn mich Mal einer richtig aufklärt , ich verstehe das hier Gerade nicht was ich zu tun habe
 
Zuletzt bearbeitet:

Plauzi92

Aktives Mitglied
Was willst du Mir damit sagen, warum brauche ich eine Instanz vom see Klasse Field oder was meinst du Mir "Typ Field"? Und wie Gebe ich jetzt genau einem Feld die Position not?
Du hast doch eine Klasse Field oder nicht? Aus dieser werden die Instanzen (also im Prinzip deine einzelnen Mauern und Wege) erstellt. Jede dieser Mauern/Wege hat einen X Wert und einen Y Wert woraus sich die genau Position in dem Labyrinth ergibt. Position (0|0) wäre demnach oben links in der Ecke.
Außerdem hast du den FieldType. Das kann ein String, oder ein Integer ( 0 für Weg, 1 für Mauer) oder auch irgendwas anderes sein, dass dir gefällt und Sinn macht.

Wenn du also ein neues Field erstellst, kannst du durch den Konstruktor direkt die Parameter mitgeben.

Die Klasse Field sähe dann so aus:

Code:
public class Field {
private int PositionX, PositionY;
private String Type;

public Field(int PositionX, int PositionY, String Type) {    // <- Das ist der Konstruktor
        this.PositionX = PositionX;
        this.PositionY = PositionY;
        this.Type = Type;
    }
}

Und beim erstellen eines neuen Fields an der Position (20|20) vom Typ WALL gibst du das direkt mit:

Code:
Field neuesField = new Field(20,20,"WALL");
 
K

kneitzel

Gast
Also ein paar Punkte:

a) Ich denke, dass es schon wichtig ist, dass sich mehrere Personen beteiligen, denn ein Einzelner kann immer mal keine Zeit haben zum antworten.

b) Plauzi92 hat einfach eine Art Optimierung gemacht. So der Server richtig funktioniert, werden alle Wege durch Mauern abgegrenzt sein. Diese "Unknown" Felder dürften den Client also nicht interessieren. Daher optimiert er so, dass er einfach nur die Mauern speichert. Kann man so machen, aber die Frage ist, ob man damit die Erwartungshaltung bezüglich der Aufgabe erfüllt. In einem realen Projekt sollte die Analyse so sein, dass man klar festlegt, was gebraucht wird. Wenn da 3 Typen von Feld definiert sind, dann würde ich diese auch so umsetzen. Hintergrund ist, dass bei agilen Arbeiten das halt erst ein Punkt ist und es kommen noch weitere Entwicklungsschritte hinterher.
Oder anders gesagt: Dann hätte ich diese Optimierung auch schon direkt beim Server erwartet....

c) Generell führen viele Wege zum Ziel. Du kannst den Konstruktor durchaus so schreiben, wie Du ihn zuletzt geschrieben hast. Worauf ich hinaus wollte, war:
Du wirst beim Client das Labyrinth abfragen und es zurück bekommen. Die Rückmeldung vom Server verwendest Du, um z.B. ein 2d Array von Field zu füllen. Zum Füllen rufst Du also für jede Position des 2d Arrays ein Konstruktor auf. Jeder Aufruf wird also eine neue leere Liste für die Baits übergeben.
Aber das funktioniert natürlich:
felder[x][y] = new Field(getFieldType(scannedType), new ArrayList<BaitType>());

Das wäre dann die Nutzung Deines Konstruktors. Aber dann stellst Du fest:
1) getFieldType(scannedType) kann von der Funktionalität in Field einfließen oder in FieldType. Das nennt sich dann Refactoring. Kann man machen, aber zur Not ist so Code erst einmal an anderer Stelle ... alles prinzipiell ok. Das Thema kann man nach Erstellung einer Lösung angehen.
2) Du rufst den Konstruktor immer mit new ArrayList<BaitType>() auf. Dann kannst Du auf diesen Parameter verzichten und im Konstruktor statt dessen diesen Code schreiben. Dann hättest Du etwas wie:
Java:
private Field(FieldType fieldType) {
        this.fieldType = fieldType;
        this.baittypes = new ArrayList<BaitType>();
    }

Aber auch hier gilt: Kann man machen, aber das wäre auch ein Punkt, den man nach einer ersten Lösung machen kann (so man so gewisse Dinge nicht sieht).

Um so Refactorings ggf. zu vermeiden kann man Funktionalität sozusagen "Top - Down" entwickeln. Das ist generell üblich. In der Regel findet dies aber schon bei der Auflistung der Tasks statt: Was wird wofür gebraucht? Daraus entstehen dann die klaren Aufgaben, was z.B. für "Field" gebraucht wird. Oder man macht es einfach Top/Down bei der Entwicklung. Was brauchst Du denn von Field? Du startest oben bei der Implementierung nachdem Du einen groben Plan hast. Wie der Konstruktor genau aussieht, ist Dir erst einmal egal. Lass ihn zur Not erst einmal weg.
Wenn Du dann die ersten Field Instanzen erzeugen musst, dann siehst Du, was Du hast: Das kann dann also ein String sein mit der Darstellung des Feldes. Und dann gibt es halt einen Konstruktor, der einfach nur so einen String entgegen nimmt.... So vermeidest Du, unnötig Zeit zu verschwenden.
Aber ganz wichtig: Der grobe Aufbau und wo welche Daten gespeichert werden: Das musst Du von Anfang an festlegen. Ohne so ein Design kommst Du nicht weit. Aber das hast Du und da würde ich jetzt nicht zu viel Zeit rein stecken und das ggf. "on the fly" erweitern in den Details.

Ach ja: Der neue Punkt "Position des Feldes":
Nimm das Beispiel Bücherregal: Ein Buch steht im Bücherregal. Wie in der Bücherei: Platz B46 ist von diesem Buch.
Jetzt kann man das natürlich auch im Buch vermerken. Aber muss man das? Das wäre wichtig, wenn man die Position ständig benötigen würde: Du hast das Buch heraus genommen und nun muss man es zurück stellen. Dann braucht man das.
Aber wenn Du nun Bücher in ein regal stellst: Dann hast Du natürlich auch die Bücher an speziellen Positionen. Über die Position kannst Du auf die Bücher zugreifen. Im Buch muss die Position nicht stehen.

Wenn Du Blätter auf den Schreibtisch legst: Du notierst ja nicht die Position auf den Blättern. Aber wenn Du die Blätter brauchst: Kein Thema: Die Blätter liegen da und da auf dem Schreibtisch. -> zugriff klappt auch ohne dass der Ort vermerkt wird.

So sehe ich das hier auch. Wenn Du z.B. ein 2d Array mit Feldern hast, dann greifst Du darüber auf das jeweilige Feld zu. Aber den Feldern ist es egal, an welcher Position sie sind. Es sind einfach Feld-Instanzen und der Ort ist durch die Referenzierung im 2d Array festgelegt.
 
K

kneitzel

Gast
Du hast doch eine Klasse Field oder nicht? Aus dieser werden die Instanzen (also im Prinzip deine einzelnen Mauern und Wege) erstellt. Jede dieser Mauern/Wege hat einen X Wert und einen Y Wert woraus sich die genau Position in dem Labyrinth ergibt. Position (0|0) wäre demnach oben links in der Ecke.
Außerdem hast du den FieldType. Das kann ein String, oder ein Integer ( 0 für Weg, 1 für Mauer) oder auch irgendwas anderes sein, dass dir gefällt und Sinn macht.

Wenn du also ein neues Field erstellst, kannst du durch den Konstruktor direkt die Parameter mitgeben.

Die Klasse Field sähe dann so aus:

Code:
public class Field {
private int PositionX, PositionY;
private String Type;

public Field(int PositionX, int PositionY, String Type) {    // <- Das ist der Konstruktor
        this.PositionX = PositionX;
        this.PositionY = PositionY;
        this.Type = Type;
    }
}

Und beim erstellen eines neuen Fields an der Position (20|20) vom Typ WALL gibst du das direkt mit:

Code:
Field neuesField = new Field(20,20,"WALL");

Sorry, von dem Aufbau halte ich so erst einmal nichts. Du hast doch ein klaren 2d Aufbau fester Größe. Desweiteren sollte man bei einem Objektorientierten Ansatz bleiben und sich von den Native Types lösen wo es nur geht. Und dieses Lösen könnte man über mehrere Wege machen:

Ein erster Schritt ist erst einmal, die Literale in Konstanten umzuwandeln. Dann hast Du statt "WALL" halt irgend eine Konstante a.la. Field.TYPE_WALL oder so.
Dannkommt aber sofort der zweite Refactoring-Schritt:Du hast mehrere Konstanten, die zusammen gehören und Du hast eine Variable, die nur bestimmte Werte annehmen kann: Also kommt direkt das Enum.

Dann muss Code an die richtige Stelle. Der Typ eines Feldes sollte also gewisse Dinge wissen wie
- kann ein Spieler auf das Feld? (So es nur vom Typ abhängt)
- wie wird es vom Server dargestellt?

Dann ist klar, wo genau dieser Code steht. Er ist an einer klar definierten Stelle und man hat Verantwortung nicht künstlich in anderen Klassen abgelegt.

Das ist aber nur meine Sicht. Deine Lösung wird auch funktionieren. Aber wie gesagt: Mir sagt die so nicht zu und sobald man es weiter ausbaut wird es schnell unnötig komplex und damit unwartbar.
 

mrBrown

Super-Moderator
Mitarbeiter
Ist an der Position (X/Y) ein Objekt Field mit dem Typ "WALL"? -> Ja? -> Nicht setzen
Ist an der Position (X/Y) ein Objekt Wall? ->Ja? -> Nicht setzen

Macht doch absolut keinen Unterschied, nur dass man sich bei letzterer Variante den FieldType spart.

Natürlich macht das einen Unterscheid, das sind völlig unterschiedliche Designs:

1598386379081.png1598386545455.png1598386489786.png

Möglich sind X verschiedene Varianten, aber Unterschiede macht das in jedem Fall.


Den Konstruktor bräuchtest du doch auch ohne FieldType damit du der Instanz vom Typ Field die Position direkt mitgeben kannst.
Allerdings muss ein Feld eigentlich nie seine Position wissen. Wenn es die weiß, hat man die Information doppelt gespeichert, was zu Problemen führen kann und oft auch führt.
 

jono

Top Contributor
Wenn Du dann die ersten Field Instanzen erzeugen musst, dann siehst Du, was Du hast: Das kann dann also ein String sein mit der Darstellung des Feldes. Und dann gibt es halt einen Konstruktor, der einfach nur so einen String entgegen nimmt....
1.
Ein String zur Darstellung des Feldes? Ein Feld wird doch durch eine array position lokalisiert und hat einen FieldType vom Typ Enumeration, verstehe ich jetzt nicht ganz.
2.
Wie erzeuge ich den Field Instanzen, bzw. wo sollen die erzeugt werden? Was ich auch nicht verstehe, was konkret an einem Beispiel von mir aus, heißt es, dass ein Konstruktor entgegen nimmt?
3.
Essentiell noch mal für mich wäre zu klären, welche Aufgabe der Konstruktor jetzt konkret erfüllen soll? Ein Konstruktor dient ja eigentlich nichts anderem als der Erzeugung von Objekten. Wäre super wenn mir das jemand evtl. an einem Fallbeispiel erklären kann. Also die Informationen, die der Server dem Client übergibt bei der Anforderung des MAZE, sind ja im Endeffekt Objekte, aber wie bringe ich das jetzt in Einklang mit dem Konstruktor, weil wenn ich mich richtig entsinne, dann war der Konstruktor doch mit dem Erhalt der Informationen in Zusammenhang gebracht worden?
 

jono

Top Contributor
Ach ja: Der neue Punkt "Position des Feldes":
Nimm das Beispiel Bücherregal: Ein Buch steht im Bücherregal. Wie in der Bücherei: Platz B46 ist von diesem Buch.
Jetzt kann man das natürlich auch im Buch vermerken. Aber muss man das? Das wäre wichtig, wenn man die Position ständig benötigen würde: Du hast das Buch heraus genommen und nun muss man es zurück stellen. Dann braucht man das.
Aber wenn Du nun Bücher in ein regal stellst: Dann hast Du natürlich auch die Bücher an speziellen Positionen. Über die Position kannst Du auf die Bücher zugreifen. Im Buch muss die Position nicht stehen.

Wenn Du Blätter auf den Schreibtisch legst: Du notierst ja nicht die Position auf den Blättern. Aber wenn Du die Blätter brauchst: Kein Thema: Die Blätter liegen da und da auf dem Schreibtisch. -> zugriff klappt auch ohne dass der Ort vermerkt wird.

So sehe ich das hier auch. Wenn Du z.B. ein 2d Array mit Feldern hast, dann greifst Du darüber auf das jeweilige Feld zu. Aber den Feldern ist es egal, an welcher Position sie sind. Es sind einfach Feld-Instanzen und der Ort ist durch die Referenzierung im 2d Array festgelegt.
Okay, danke gutes Beispiel!
 
K

kneitzel

Gast
Zu 1: schau dir an, wie das Spiel abläuft. Der Client fragt den Server nach dem Maze. Der Server schickt dann das Maze: was genau sendet der Server zurück? Aus dem, was der Server zurück sendet musst du ja deine Daten aufbauen....

Zu 2. Du musst dir den Ablauf genau ansehen. Die Kommunikation zwischen Client und Server ist ja genau spezifiziert. Wie läuft denn dann so ein Spiel ab? Gev das einmal in Ruhe durch um eine Vorstellung davon zu bekommen, wann der Client was macht und was für Daten er verarbeiten muss.

Zu 3. Der Konstruktor dient der Initialisierung von neuen Instanzen. Für das Erzeugen ist das new verantwortlich. Wenn mit new eine neue Instanz einer Klasse erzeugt wird, wird der angegebene Konstruktor zur Initialisierung aufgerufen.
Dabei können Daten für die neue Instanz übergeben werden.
 

jono

Top Contributor
Zu 1: schau dir an, wie das Spiel abläuft. Der Client fragt den Server nach dem Maze. Der Server schickt dann das Maze: was genau sendet der Server zurück? Aus dem, was der Server zurück sendet musst du ja deine Daten aufbauen....
Ja, der Server sendet String commands die der CommandHandler in GameStatusModel updatet.


Zu 2. Du musst dir den Ablauf genau ansehen. Die Kommunikation zwischen Client und Server ist ja genau spezifiziert. Wie läuft denn dann so ein Spiel ab? Geh das einmal in Ruhe durch um eine Vorstellung davon zu bekommen, wann der Client was macht und was für Daten er verarbeiten muss.
Warum den Ablauf ansehen, den habe ich ja schon genau verstanden! Mir geht es jetzt genau um den Konstruktor, ich bekomme Informationen vom CommandHandler aufgrund eines ServerBefehls. Dieser aktualisiert Daten in GameStatusModel, sei es Field oder Player...
Wenn es Field ist, werden z.B. Baits oder fieldTypes aktualisiert, heißt es werden neue Instanzen erzeugt oder wie ??? Ja im Prinzip schon aber wie werden die erzeugt, ich kenne das so dass ich z.B. Instanzen einer Klasse in einer anderen Klasse erzeuge bzw. in einer Main-Klasse. Ich kenne das nicht so wie das hier gemacht werden soll. Kannst du mir da konkreter behilflich sein?

Zu 3. Der Konstruktor dient der Initialisierung von neuen Instanzen. Für das Erzeugen ist das new verantwortlich. Wenn mit new eine neue Instanz einer Klasse erzeugt wird, wird der angegebene Konstruktor zur Initialisierung aufgerufen.
Dabei können Daten für die neue Instanz übergeben werden.
Ja, das ist mir alles bewusst, wichtig ist mir wirklich zu wissen wie der Konstruktor jetzt ganz konkret seine Aufgabe erfüllen soll..

Noch eine Frage:
Java:
private FieldType fieldType;
    private List<BaitType> baittypes = new ArrayList<BaitType>();
Eine Instanzvariable fieldType brauche ich dann jetzt nicht mehr ? Weil das wurde ja abgetan von euch, es hieß erst diese darf nicht null sein, dann habe ich sie ungleich null gemacht und das war dann auch falsch gewesen.
 

jono

Top Contributor
1. Wie implementiere ich Properties die einen Zustand speichern,
2. Was ist da wieder genau mit Zustand gemeint, statische Variablen und enum werte auch oder?
 

jono

Top Contributor
Also wie ich die Properties in GameStatus Model implementiere ist klar, aber wie setze ich das in JavaFX um, wobei ich denke, dass ich die Frage nochmal stelle wenn ich das FX-Subsystem angefangen habe
 
K

kneitzel

Gast
Sorry, aber ich bezweifle, dass Du das wirklich überblickst.

Irgendwo wirst Du hoffentlich die Felder des Labyrinths speichern wollen. Und diese Stelle musst Du aktualisieren, wenn Du die Daten vom Server bekommst, die ja so aussehen sollen:
Code:
MAZE;20;10
--------------------
-##################-
-#................#-
-#.###.#.###.##.#.#-
-#...###..#..#..#.#-
-#.#.........#..#.#-
-#.#########.#.##.#-
-#................#-
-##################-
--------------------

Also musst Du aus diesem Text irgendwie die Daten zusammen bekommen. Das könnte z.B. sein, dass Du ein 2d Array von Field erstellst mit der entsprechenden Größe also z.B. hier [20][10].
Und dazu musst Du dann die Zeichen auswerten. Aus dem '-' Zeichen soll ein Field werden vom Typ UNKNOWN. Aus dem'.' Zeichen ein Field vom Typ PATH und aus dem '#' ein Field vom Typ WALL (Oder wie die Typen bei Dir auch immer heißen - habe ich jetzt nicht nachgeschlagen!)

Also brauchst Du ganz offensichtlich Code, der aus einem Zeichen ein Field erstellt. Das könnte man über einen Konstruktor machen. Aber das kann man auch gerne anders machen - das ist mir auch relativ egal. Mach es so, wie Du es machen möchtest, aber MACH EINFACH. Setz Dich dran und schau, wie Du das erstellt bekommen kannst. Und wenn Du alles in eine zentrale Klasse packst die dann direkt auf den anderen Klassen agiert. Das kann man später anpassen (Refactoring). Nur mach die Logik! Dann siehst Du auch, was Du brauchst.

Und auf die Frage, ob man den FieldTyp braucht, da habe ich jetzt einfach keinen Ansatz mehr. Wir haben da nun wirklich lang und breit drüber gesprochen. Du hast den Typ auf einen festen Wert gesetzt. Das ist Quatsch, denn alle Felder sind ja nicht vom gleichen Typ! Und natürlich muss der Typ gesetzt werden, denn ein Feld ist entweder vom Typ UNKNOWN, PATH oder WALL. Also hast Du eine Variable, die gesetzt sein muss: Dann setz diese doch im Konstruktor. Und da es unterschiedliche Felder mit unterschiedlichen Typen gibt: Übergib etwas, so dass Du den Typ setzen kannst.

Das kann also einfach nur der FieldType sein, den du direkt übergibst. Oder Du übergibst das Zeichen und dann sucht der Konstruktor raus, was für ein Typ das ist. Das kann z.B. in einem switch passieren wenn Du das so machen möchtest.

Was die Properties angeht: Nutz einfach die entsprechenden Property Klassen. Also erstell eine Instanz von StringProperty und schon kannst Du ein Control an diese Property binden. Aber da einfach mal ein paar Beispiele zu JavaFX ansehen. Das ist ja nun ein Gebiet, wo es im Internet sehr viele Seiten zu gibt....
 

jono

Top Contributor
Und da es unterschiedliche Felder mit unterschiedlichen Typen gibt: Übergib etwas, so dass Du den Typ setzen kannst.
Was soll das heißen? Wie übergebe ich etwas einem Konstruktor, die Begriffe die du da verwendest geben mir kein Aufschluss darüber?Was soll ich übergeben, was heißt ein Typ "setzen"?

Du hast den Typ auf einen festen Wert gesetzt. Das ist Quatsch, denn alle Felder sind ja nicht vom gleichen Typ! Und natürlich muss der Typ gesetzt werden, denn ein Feld ist entweder vom Typ UNKNOWN, PATH oder WALL. Also hast Du eine Variable, die gesetzt sein muss: Dann setz diese doch im Konstruktor.
Ja ich habe doch eine Variable namens vom Typ FieldType in den Konstruktor gesetzt?
 
Zuletzt bearbeitet:

jono

Top Contributor
Also brauchst Du ganz offensichtlich Code, der aus einem Zeichen ein Field erstellt. Das könnte man über einen Konstruktor machen.
Wie macht man das mit einem Konstruktor, ich kenne nur die einfache Instanziierung von Variablen einer Klasse. Mit new wird ein Konstruktor aufgerufen. Aber wie erstelle ich dann mit einem Konstruktor aus einem Zeichen ein Field. Das ist mir einfach völlig fremd
 
K

kneitzel

Gast
Wenn eine Methode Parameter hat und dann wird die Methode aufgerufen: Dann wird da umgangssprachlich von "übergeben" gesprochen.
Also Du hast eine Methode test, die einen int Parameter hat. Dann kannst Du test aufrufen und z.B. 5 übergeben: test(5);

Einer Variable einen Wert zuweisen: Das wäre umgangssprachlich das setzen. "Typ" entsprach hier einfach deiner FieldType Variablen.

Und Du kannst einen Konstruktor schreiben wie jede Methode. Da kann also mehr drin sein als nur einfache Zuweisungen. Also sowas wie:
Java:
public Field(final char character) {
  switch (character) {
      case '.': fieldType = FieldType.WAY; break;
      case '#': fieldType = FieldType.WALL; break;
      case '-': fieldType = FieldType.UNKNOWN; break;
      default: throw new IllegalArgumentException("Character " + character + " is not valid!");
  }
}

Oder
Java:
public Field(final char character) {
  fieldType = FieldType.of(character);
}
Und in FieldType hast Du dann eine Methode
Code:
public static FieldType of(final char character) { ... }
die diese Umwandlung enthält. Und das entweder mit einem switch wie oben gezeigt oder ggf. wie schon einmal früher exemplarisch gezeigt mit einer for each Schleife und Prüfen eines Wertes, der jedem enum Eintrag zugewiesen wurde. Das suche ich jetzt aber nicht raus und Du kannsteinfach bei einem switch bleiben und gut ist es ...
 

jono

Top Contributor
@JustNobody
1. Wozu brauche ich Dann die Methoden in FieldType?

2. Nun möchte ich das Command-Pattern implementieren.
Wie Gehe ich dabei genauer vor?
Ich muss erstmal eine Klasse Command mit der execute() Methoden schreiben, die Dann von den jeweiligen Servernachrichten wie z.B. die MAZE (MAZE wird Dann eine Befehlsklasse public class Maze) implementieren wird. Als pro Servernachricht eine Befehlsklasse, die die jeweiligen Änderungen in GameStatusModel vornehmen?
 

jono

Top Contributor
?
diese Umwandlung enthält. Und das entweder mit einem switch wie oben gezeigt oder ggf. wie schon einmal früher exemplarisch gezeigt mit einer for each Schleife und Prüfen eines Wertes, der jedem enum
Warum Schreibst du es Dann erst in den Konstruktor von Field und Dann sagst du die Umwandlung soll in die Methoden von FieldType?
 
K

kneitzel

Gast
Ich habe einfach mehrere Möglichkeiten aufgezählt, wie es implementiert werden könnte.

Aber ich habe schon gesagt Spätestens in #173 ("Das könnte man über einen Konstruktor machen. Aber das kann man auch gerne anders machen - das ist mir auch relativ egal. Mach es so, wie Du es machen möchtest, aber MACH EINFACH. Setz Dich dran und schau, wie Du das erstellt bekommen kannst. Und wenn Du alles in eine zentrale Klasse packst die dann direkt auf den anderen Klassen agiert. Das kann man später anpassen (Refactoring). Nur mach die Logik!")

Das erste war einfach eine 08/15 Lösung für Dich. Einfach zu verstehen und vielleicht hast Du diese ja verstanden. Das was folgt, ist ein Refactoring - also eine Art Verbesserung, denn Code, der den FieldType betrifft sollte eigentlich auch da drin zu finden sein. Field sollte keine Entscheidungen treffen, die FieldType zustehen, also auch nicht, welches Zeichen denn nun welchem FieldType entspricht. Aber das ist nebensächlich. Bau nur einfach eine Lösung, die Du verstehst.
 

jono

Top Contributor
Das wäre dann soweit geklärt.
Jetzt möchte ich den CommandHandler implementieren, d.h. die jeweiligen eingehenden Servernachrichten in ein Befehlsobjekt konvertieren. Dann sollen verschiedene Befehlsklassenn pro Servernachricht erstellt werden, um GameStatusModel über Änderungen zu informieren. Das Ganze stellt ja das Command-Pattern dar.
Wie genau erstellt man jetzt die Befehlsobjekte die auf die jeweilige Befehlsklasse verweisen sollen?
Die jeweiligen Servernachrichten sind ja I'm CSV Format gegeben. Diese müssen eingelesen werden als String-Datentyp.
Meine Frage:
Wie genau gehe ich jetzt vor, wie verknüpfe ich die Blickrichtung (z.B. die 2. Position eines CSV-Datensatzes wie bei der PPOS Servernachricht in der exam-task.pdf) mit einer Befehlsklasse, welche die Blickrichtung ändern kann?
In der Befehlsklasse selbst wird die Blickrichtung geändert, nachdem die Nachricht ausgelesen würde und auf die jeweiligen Befehlsklassen verweist...
Kann mir da jemand konkret behilflich sein und mir gute Tipps gehen ? 🙂
 

jono

Top Contributor
@JustNobody
Java:
if (parts[0].equals("PSCO")){
                
            }
das parts-Array ist das CSV-Array welches die Servernachricht enthält und durch ";" gesplitet wird.
Nun möchte ich der if-Anweisung sagen, wenn parts[0] "PSCO" ist, dann möchte ich dass die Command Klasse "X" angesprochen wird, bzw. ihre Methdoe ausführt?
 
K

kneitzel

Gast
@JustNobody
Java:
if (parts[0].equals("PSCO")){
               
            }
das parts-Array ist das CSV-Array welches die Servernachricht enthält und durch ";" gesplitet wird.
Nun möchte ich der if-Anweisung sagen, wenn parts[0] "PSCO" ist, dann möchte ich dass die Command Klasse "X" angesprochen wird, bzw. ihre Methdoe ausführt?

Hast Du eine Instanz der Klasse "X" oder ist die Methode statisch? Dann kannst Du da den Aufruf einbauen. (Statisch ist aus Sicht von Java technisch möglich, aber aus Sicht der Objektorientierung ist es eher keine Option. Wobei ich jetzt nicht den Überblick habe, was Du da wie an Code hast und so.

Anregung (Wenn Du es nicht verstehst, dann bau es erst so, wie Du es verstanden hast - das kannst Du komplett ignorieren):
Generell scheint mir das schon fast etwas zu sein, dass man recht schön aufbauen kann mit einer Map also etwas in der Art:
commandMap.get(parts[0]).execute()
Die commandMap hat dann halt den Zusammenhang zwischen "PSCO" und einer "Command Klasseninstanz" und ich habe da die Methode, die aufgerufen wird, einfach mal execute genannt. Das kann natürlich in Deinem konkreten Fall etwas anders aussehen.

Oder wie schon so oft auf den Seiten dieses Threads erwähnt: Enum kann man auch nutzen, um sowas aufzubauen (So die Elemente fix sind, denn im Gegensatz zu der map kann man einer Enum zur Laufzeit keine Member hinzufügen).
 

jono

Top Contributor
Den oben genannten commandMap-Befehl würde ich dann in die if-Anweisungen schreiben. Die if-Anweisungen kommen in die Klasse des MazeGameProtocol's, die die Servernachrichten einlesen. Der Code hier stellt ja die Befehlsklasse dar, die die PSCO Nachricht aktivieren soll. Wenn ich das jetzt so mache:
Java:
if (parts[0].equals("PSCO")){
            commandMap.get(parts[0]).execute()    
            }
wäre das ja noch nicht korrekt, wie mache ich das jetzt, dass die ScoreCommand-Befehlsklasse angesprochen wird.
Java:
public class ScoreCommand implements Command {

    private int score = parts[2];

    @Override
    public int execute() {

        return score.addToScore();
    }

}
addToScore ist dann hier die Methode aus der Empfängerklasse, wie z.B. die Playerklasse in der der Score verändert werden soll.
Die Befehlsklasse "ScoreCommand" soll ja jetzt nur die addToScore-Methode in "Player" ausführen, ist ja die Aufgabe einer Befehlsklasse...
Wie mache ich jetzt hier richtig, eher gesagt wie kann ich der Befehlsklasse das parts-array ansprechen, welches ich in der Readerklasse des MazeGameProtocols definiert habe:
Java:
try(
    LineNumberReader read = new LineNumberReader(new StringReader())){
        read.lines()
        .filter(line -> read.getLineNumber() = 0)
        .forEach(line -> {
            String [] parts = str.split(";");
            
            if (parts[0].equals("PSCO")){
                commandMap.get(parts[0]).execute();
 
K

kneitzel

Gast
Also ich habe keinen Überblick über dein Design und kann daher nur sagen:
a) Das mit der map hast Du falsch verstanden. Da brauchst Du dann kein if mehr! Das ist ja der Sinn dahinter - eine Serie von ifs bzw ein switch zu vermeiden.
b) Deine Klasse ScoreCommand macht keinen Sinn. So soll denn da das parts[2] her kommen?
c) Wieso meinst Du, da plötzlich mit Streams arbeiten zu müssen? Du scheinst noch Probleme mit den Java Basics zu haben - da würde ich dies erst einmal nicht nutzen. Und was genau willst Du da genau machen? Ich würde das an Deiner Stelle mit kleinen, sinnvollen Methoden machen. Und was ist da für eine Bedingung in .filter? Soll das == oder != sein? Das = alleine macht so keinen Sinn. Und wenn es ein Stream sein soll: Dann mach es wirklich einfach zu lesen - ebenfalls mit Methoden. Also sowas wie:
Java:
try(LineNumberReader read = new LineNumberReader(new StringReader())) {
    read.lines()
    .filter(line -> read.getLineNumber() != 0)
    .forEach(line -> verarbeiteZeile(line));
}
Ich habe da mal ein != 0 gemacht - willst evtl. die erste Zeile nicht haben oder so ... Und dann klar sauber Methoden aufgebaut. Wobei ich statt dem Lambda Ausdruck eine Methodenreferenz angegeben hätte, aber das mit dem Lambda ist evtl. lesbarer für Dich ...
 

jono

Top Contributor
Zu a)
Okay, also die Reader-Klasse in MazeGameProtocol kann dann durch den commandMap-Befehl direkt die Methode "execute" in der Befehlsklasse ausführen?
Zu b)
Ja, ich weiß, dass das keinen Sinn macht. Das Problem ist, dass ich die 3. Position für den Punktestand benutzen muss weil das ja so vorgegeben ist. Und den brauche ich ja für die Methode addToScore, die ich in der Playerklasse schreibe. Also die dritte stelle des PSCO Befehls muss ja für die Methode verwendet werden, darum geht es mir da.
Zu c)
Weil die Servernachrichten eingelesen werden müssen?
Ja sinnvoll wäre da eher >= 0.
 

jono

Top Contributor
@JustNobody Ich würde es gerne mit den if Anweisungen so fortsetzen...
Java:
if (parts[0].equals("PSCO")){
                ScoreCommand psco = new ScoreCommand();
Das würde dann erstmal so aussehen. Dieser Code steht in einer Klasse der MazeGameProtocol-Komponente. Wie schaffe ich es jetzt in der Komponente COmmandHandler mehrere Verweise zu setzen auf die Objekte, denn ich muss ja natürlich noch mehr Befehlsklassen als ScoreCommand erstellen
 

jono

Top Contributor
Also wie übergebe ich die Referenzvariable einer z.B. Klasse namens CommandHandler im CommandHandler.
Java:
ScoreCommand score = psco
So gebe ich "score" eine Referenz, die auf dasselbe Objekt verweist, nur wie übergebe ich diese Referenzvariable psco, weil diese kennt das Programm ja nicht bzw. erscheint die Meldung: psco cannot be resolved to a variable
 

Ähnliche Java Themen

Neue Themen


Oben