OOP Zugriff auf eine Klasse aus einem Interface-Objekt

der_roemer

Mitglied
Hallo!

Ich weiß nicht so recht, wonach ich das Internet fragen soll
bei meinem Problem...

Ich will TicTacToe programmieren,
mit einem Strategy Pattern für die Züge 1,2,3,n
weil sich das Vorgehen bei jedem Zug vom anderen unterscheidet
und ich IF-Bäume vermeiden will.

Ich hab eine SPIEL-Klasse und erzeuge daraus meinSpiel

Java:
class Spiel {

    private IZug welcherZug;
    private int[][] spielfeld;
    public static Spielfigur spielfigur;
    
    public Spiel(){
        spielfeld = new int[3][3];
        welcherZug = new Zug1();
        spielfigur = new Spielfigur(0, 0, 0);
    }

    public void setWelcherZug(IZug welcherZug) {
        this.welcherZug = welcherZug;
    }
    
    public void zugAusfuehren(){
        // ich übergebe eine Objekt vom Typ Spielfigur, da ich es mit mehreren Werten
        // zu tun habe, welche ich später schreiben muss
        welcherZug.ziehen(spielfigur);
    }

    void setzeFigur(int x, int y, int besitzer) {
        if (spielfeld[x][y] == 0) {
                spielfeld[x][y] = besitzer;
        } else {
            // Spielfeld ist nicht leer !!!
        }
        
    }

    Spielfigur sucheFreiesFeld() {
      // Hier Code
    }

}

Ich habe mein Interace IZug

Java:
public interface IZug {
    public void ziehen(Spielfigur spielfigur);
}

Und hier ist mal die Klasse Zug1

Java:
public class Zug1 implements IZug{

    @Override
    public void ziehen(Spielfigur spielfigur) {
        // und hier würde ich gerne auf meinSpielfeld zugreifen
       // spielfigur = meinspiel.sucheFreiesFeld();
        
        
        
    }
}

In der Main starte ich das Spiel und will Züge ausführen.


Java:
public class Main {
    final static int leer = 0;
    final static int Blech**** = 1;
    final static int ich = 2;
    
    public static void main(String args[]){
    Spiel meinSpiel = new Spiel();
    
    meinSpiel.setWelcherZug(new Zug1());
    //hier könnte ich auch eine neue Figur übergeben
    meinSpiel.setzeFigur(0,0,ich);
    meinSpiel.zugAusfuehren();
    
    }
}

Aber wenn jetzt zugausführen()
in der Klasse Spiel auf die Klasse Zug1
zugreift, ähh, dann komm ich von da nicht mehr
an mein Spielfeld ran....



Java:
public class Zug1 implements IZug{

    @Override
    public void ziehen(Spielfigur spielfigur) {
        // und hier würde ich gerne auf meinSpielfeld zugreifen
       // spielfigur = meinspiel.sucheFreiesFeld();
    }
}

Wie könnte ich das anstellen?
 
Zuletzt bearbeitet von einem Moderator:
So wie ich das verstehe ist die Frage hier: Wie greife ich in einer Klasse/einem Objekt auf ein anderes Objekt zu. Antwort: Ganz einfach, das eine Objekt muss das andere kennen, also muss es irgendwie übergeben werden. Sind eigentlich Grundlagen der OOP.
Wenn Du Dich aber schon mit Entwurfsmustern auseinandersetzt, möchte ich Dir das MVC Pattern ans Herz legen. Es ist besonders für die Spieleprogrammierung sehr sinnvoll.
 
wenn du nur die Spielfigur übergibst, eine Klasse die du nicht gepostet hast, dann müsste dort ein Link auf das Spielfeld sein,

oder das Spielfeld ist unsauber global über eine statische Variable erreichbar

oder du übergibst einen weiteren Parameter Spielfeld

oder mein Vorschlag:
erstelle eine Basisklasse von allen Zügen, Interface evtl. nicht mehr nötig, dort hinterlege allgemeine Funktionalität wie
die Initialisierung mit wichtigen Daten wie Spielfeld,
nicht unbedingt im Konstruktor, da müsste dann in allen Subklassen auch ein Konstruktor angegeben werden,
alternativ eine init()-Methode,

je nachdem wer wo wann Zug-Objekte erstellt ist die Initialisierung dort dann nicht ganz ohne, zusätzliche Aufrufe, können vergessen werden usw.,
falls es tatsächlich eine Baumstruktur gibt besteht hoffentlich eine zentrale Stelle dafür,
z.B. in setWelcherZug() oder zugAusfuehren() von Spielfeld

jedenfalls könnten die Subklassen dann auf Methoden der Oberklasse wie getSpielfeld() zugreifen,
vielleicht ist auch die Spielfigur schon gesetzt
 
Das er die Grundzüge der OOP zumindest ansatzweise verstanden hat, dürfte die Gliederung seines Quelltextes zeigen.

Bis ich SlaterBs Kommentar gelesen hatte, hatte ich eigentlich eine abstrakte Klasse statt des Interfaces:
Java:
public abstract class AbstractZug{

     private Spielfeld feld;


     public AbstractZug(Spielfeld feld){
           this.feld=feld
     }

     public abstract void ziehen(Spielfigur spielfigur);

}

favorisiert.
Neue Zugimplementierungen müssen dann die Methode ziehen überschreiben und zumindest vom Konstruktor der Abstrakten Klasse erben.


Aber irgendwie...hmm....gefällt mir das noch nicht so wirklich.


Wieso ist die SPielfigur static?
 
Zuletzt bearbeitet:
@Michael
Ja, ich hab den Überblick verloren. vielleicht sollte ich erst einmal bei den Grundlagen bleiben und später die Patterns überfallen^^

@SlaterB
Die Klasse Spielfigur enthält nur die Eigenschaften der Figur, also Position und Besitzer. Ich will das Spielfeld nicht in der Figur haben, weil die Figur eigentlich nichts davon wissen sollte. Das mit der Basisklasse und den Zügen in Subklassen ist natürlich eine Lösung.

Die Erstellung der Zug-Objekte will ich in der Main lassen, denn da "befindet" sich ja auch der Spieler. Ich versuche die Realität so weit wie möglich abzubilden, damit ich nicht den Überblick verliere.

@derFeivel
Tja wieso ist die Spielfigur static...?
ich habs vergessen. Mit Sicherheit ein
AnfängerProbiertRumProblem
 
Ich habe das Ganze neu aufgelegt,
erst mal trocken im Editor und dann mit Netbeans,
und dann plötzlich hab ich mich gewundert, warum ich bei ZugNr1
mein Spielfeld nicht mehr sehe, aber dann hab ich es mit dem Konstruktor übergeben
und so funktioniert es jetzt. Wie ist eure Meinung dazu?
Kann man das anders/besser lösen?
Ich frag mal anders: Gibts grobe Fehler?

Hauptklasse
Java:
public class TicTacToeVererbung2 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        TicTacToeVererbung2 TTT = new TicTacToeVererbung2();
        TTT.testen();
    }

    private void testen() {
        final int leer = 0;
        final int mensch = 1;
        final int blech**** = 2;

        Spiel meinSpiel = new Spiel();
        meinSpiel.setzeSpielsteinSpiel(new Spielstein(0, 0, mensch));
        Zug1 zugNr1 = new Zug1(meinSpiel);
        zugNr1.ziehen(blech****);
        meinSpiel.spielfeld.spielfeldAusgeben();
        Zug2 zugNr2 = new Zug2(meinSpiel);
        zugNr2.ziehen(blech****);
    }
}

Zug1-Klasse

Java:
public class Zug1 extends Spiel {
    
    private Spielstein spielstein;

    Spiel meinSpiel;
    
    Zug1(Spiel meinSpiel) {
        this.meinSpiel = meinSpiel;
    }
    
    void ziehen(int spieleramZug) {
        while (spielstein == null) {
            spielstein = meinSpiel.spielfeld.findeZufaelligfreiesFeld();
        }
        spielstein.setBesitzer(spieleramZug);
        meinSpiel.setzeSpielsteinSpiel(spielstein);
    }
}
]

Spielfeld-Klasse

Java:
public class Spielfeld {

    final int leer = 0;
    final int mensch = 1;
    final int blech**** = 2;
    private int[][] spielfeldXY;

//    0,0  1,0  2,0
//    0,1  1,1  2,1
//    0,2  1,2  2,2
    public Spielfeld(int[][] spielfeldXY) {
        this.spielfeldXY = spielfeldXY;
    }

    public Spielfeld() {
    }

    public Spielfeld(int x, int y) {
        this.spielfeldXY = new int[x][y];
    }

    public boolean spielfeldfrei(Spielstein spielstein) {
        int posX = spielstein.getPosX();
        int posY = spielstein.getPosY();
        if (spielfeldXY[posX][posY] == leer) {
            return true;
        } else {
            return false;
        }
    }

    Spielstein findeZufaelligfreiesFeld() {
        Spielstein spielstein = new Spielstein(0, 0, -1);
        while (true) {
            spielstein = new Spielstein(getZufallszahl(), getZufallszahl());
            if (spielfeldfrei(spielstein)) {
                return spielstein;
            }
        }
    }

// <editor-fold defaultstate="collapsed" desc="erzeuge Zufallszahl zwischen 0 und 2">
    public int getZufallszahl() {
        int zufallszahl;
        zufallszahl = (int) (Math.random() * 3);
        return zufallszahl;
    }

// </editor-fold>
    void setzeSpielsteinSpielfeld(Spielstein spielstein) {
        int posX = spielstein.getPosX();
        int posY = spielstein.getPosY();
        if (spielfeldfrei(spielstein)) {
            spielfeldXY[posX][posY] = spielstein.getBesitzer();
        }
    }

    void spielfeldAusgeben() {
        for (int y = 0; y < 3; y++) {
            for (int x = 0; x < 3; x++) {
                System.out.print(" " + spielfeldXY[x][y] + " ");
            }
            System.out.println("");
        }

    }
}

Spielstein-Klasse
Java:
public class Spielstein {

    private int posX;
    private int posY;
    private int besitzer;

    public Spielstein(int posX, int posY, int besitzer) {
        this.posX = posX;
        this.posY = posY;
        this.besitzer = besitzer;
    }
    
    public Spielstein(int posX, int posY) {
        this.posX = posX;
        this.posY = posY;
        this.besitzer = -1;
    }

    public Spielstein() {
        this.posX = 0;
        this.posY = 0;
        this.besitzer = -1;
    }

    public int getBesitzer() {
        return besitzer;
    }

    public void setBesitzer(int besitzer) {
        this.besitzer = besitzer;
    }

    public void setPos(int posX, int posY) {
        this.posX = posX;
        this.posY = posY;
    }

    public int getPosX() {
        return posX;
    }

    public int getPosY() {
        return posY;
    }

    // zur internen Verwendung, damit die Abfrageausdrücke nicht so lang werden
    // wenn zwei Spielfelder nach Koordinaten verglichen werden sollen
    public float getPosXY() {
        float posXY = (posX + (posY / 10f));
        return posXY;
    }

    void spielsteinAusgeben(String string) {
        System.out.println(string+"--> x:"+posX+" y:"+posY+ " besitzer "+besitzer);
    }
}
 
Zuletzt bearbeitet:
Ich bin das Thema nur überflogen, aber einige Anmerkungen hab ich schonmal.

Dass deine Bezeichner nicht ganz astrein sind, dürftest du anhand der Sternchen bereits gesehen haben...
eine andere Sache ist für mich aber völlig gravierender: warum erbt Zug von Spiel? Das ergibt meiner Meinung nach (jedenfalls, ohne deine Logik zu kennen) keinen Sinn. Im Übrigen: Gibt´s auch eine Klasse Zug2 oder was soll die Numerierung?

EDIT: TicTacToeVererbung2 ist demnach ein sehr schlechter Name, nenn´s doch einfach TicTacToe... oder Katzenfutter, wäre mein Favorit

Noch ein EDIT:
// zur internen Verwendung, damit die Abfrageausdrücke nicht so lang werden
// wenn zwei Spielfelder nach Koordinaten verglichen werden sollen
public float getPosXY() {
float posXY = (posX + (posY / 10f));
return posXY;
}
Man sollte eher nicht aus Platzgründen eine so seltsame Schreibweise einführen, v.a. erstens nicht public (wenn´s nzr intern gebraucht wird) und zweitens nicht so, wenn es für int je eine einzelne Methode gibt. Das sollte schon alles konsequent sein...

Und noch was: warum gibt die Methode indezufaelligesFreiesFeld() einen Spielstein zurück? Das finde ich unnötig, gib doch einfach ein neues Punkt-Objekt aus mit den Koordinaten des Feldes...
 
Zuletzt bearbeitet:
Ja über die Sternchen hab ich auch gegrinst 😀

Zug2 gibt es auch, und dann ZugN.
Zug1 erbt vom Spiel, weil jeder Zug ein anderes Vorgehen erfordert.

Der Name TicTacToeVererbung2 kommt aus dem package und ist nur für mich,
weil ich mehrere Ansätze ausprobiert habe (patterns, siehe oben).

Diese Wurst-Methode, die float zurück gibt werd ich wieder rausschmeißen,
ist wirklich nicht elegant, aber kam mir spontan.

Hmm, das Punkt-Objekt ist ein super Vorschlag, werd ich umsetzen,
danke!!
 

Zurück
Oben