Design Problem

Status
Nicht offen für weitere Antworten.

Bit2_Gosu

Bekanntes Mitglied
Hi!

Ich bin dabei, einen Schachcomputer zu programmieren. Es gibt eine Klasse "GUI", die auf ein Objekt der Klasse "Game" zugreifen kann. Das ist nötig, denn je nachdem, was der Benutzer klickt muss Einfluss auf das Spiel genommen werden. Nun ist es so, dass ein Bauer, der an die gegnerische Grundlinie gelangt umgewandelt werden kann.
Der Bentuzer schiebt den Bauer nach vorne --> es wird auf die Klasse "Game" einfluss genommen --> diese Stellt fest, dass der Bauer eingetauscht werden kann --> es öffnet sich ein Fenster, in dem sich der Benutzer eine Figur aussuchen kann.
Nun bedeutet aber der letzte "-->", dass die Klasse "Game" auf die Klasse "GUI" Einfluss nehmen muss, denn die GUI regelt alles Graphische. Das finde ich aber unschön, denn warum sollte eine Klasse "Game" etwas von irgendwelcher Graphik wissen.

Nun habe ich mir überlegt: Die Klasse Game setzt ein flag, dass von einem Thread der GUI regelmäßig gecheckt wird. Je nachdem öffnet sich ein Fenster.

Würdet ihr das auch so kompliziert machen, oder habt ihr vielleicht eine bessere Idee?
 
Würdet ihr das auch so kompliziert machen, oder habt ihr vielleicht eine bessere Idee?
Ich finde es ein bisschen merkwürdig, dass die GUI auf Game zugreift und nicht umgekehrt. Game ist - nehme ich an - der intelligente Teil und sollte der GUI sagen setze FigurX von Position X auf Y. Würde Dir wie @x.l raten sich mit dem MVC auseinander zu setzen und Dein Programm-Design zu überdenken.
 
Ah ja danke, sehr interessant.

Ich habe mir gerade den Wikipedia Artikel zum MVC-Konzept durchgelesen und habe dazu eine Frage:

Da steht "Im Regelfall wird die Präsentation über Änderungen von Daten im Modell mithilfe des Entwurfsmusters „Beobachter“ unterrichtet".

Ich hätte jetzt aber gedacht, der Controller wird über Änderungen von Daten im Modell mithilfe des Entwurfsmusters "Beobachter" unterrichtet und bewirkt über seine Verbindung zur View eine direkte Änderung der View.

Was meint ihr dazu?

Mein Programm hat jetzt übrigens folgende Struktur: Meine Klasse "Game" ist das Modell im MVC-Konzept und kennt weder GUI (also View) noch die Kontroller-Klasse (die ActionListener erweitert). Die View kennt nicht das Modell aber kennt den Controller. Der Controller kennt View und Modell. Wenn sich das Modell nun so ändert, das auch die View geändert werden muss, gibt das Modell die Information, dass sich etwas geändert hat über eine Methode an alle anonymen (!) Beobachter weiter. Ein Beobachter wäre in diesem Fall der Controller, der so benachrichtigt wird und im Anschluss die View direkt (!) informiert.
 
Ich hätte jetzt aber gedacht, der Controller wird über Änderungen von Daten im Modell mithilfe des Entwurfsmusters "Beobachter" unterrichtet und bewirkt über seine Verbindung zur View eine direkte Änderung der View.

Was meint ihr dazu?
Ich bin der Meinung das liegt im Ermessen des Programmierers und man kann es je nach Anwendungsfall so und so halten. Sicherlich kann man es so wie oben beschrieben machen. Würde mir allerdings die Frage stellen, ob Änderungen des Models für den Controller überhaupt interessant sind. Wenn nicht (das ist wohl in den meisten Fällen) würde ich die View als Beobachter ans Model hängen, denn die muss in Regel wissen, wenn sich was am Model ändert.
 
Dass "das GUI" auf's Game zugreift ist nicht falsch oder ungewöhnlich - das erfolgt schließlich über einen Listener, und der übernimmt hier die Rolle des Controllers. Es ist lediglich wichtig, dass das Game das GUI nicht direkt kennt. Das Game sollte nicht mal wissen, dass es überhaupt ein GUI gibt (was ja z.B. der Fall sein kann, wenn zwei KIs gegeneinander spielen - dann erscheint nach 42 Tagen und 17 Stunden einfach nur auf der Console "KI0 won" - nix mit GUI)

Dem MVC-Prinzip nach würde man das auch durch einen Listener lösen. Das ist jetzt SEHR hemdsärmelig und lapidar beschrieben, aber vom Prinzip her (!) KÖNNTE das sowas sein wie
Java:
interface GameListener
{
    void pawnReachedOppositeSide(GameEvent event);
    ...
}

class SimpleGame
{
    private List<GameListener> gameListeners = ... // add/remove-Methoden hierfür

    // Wird aufgerufen, wenn ein Bauer die gegnerische Seite erreicht
    private void firePawnReachedOppositeSideEvent(Pawn pawn)
    {
        for (all GameListeners) gameListener.pawnReachedOppositeSide(someEvent);
    }
}

class GUI implements GameListener
{
    private Game game = ...

    public void pawnReachedOppositeSide(GameEvent event)
    {
        // Wichtig: MODALER Dialog!!
        Piece newPiece = selectPieceWithModalDialog();
        game.replacePiece(event.getOldPiece(), newPiece);
    }
}
 
Wobei... Jetzt, wo ich drüber nachdenke... das löst vielleicht dein angedeutetes Problem mit der Verbindung zum GUI, aber ... eigentlich ist diese Abhängigkeit vom GUI an sich ja schon nicht sinnvoll .... Ich denke, man sollte eine Ebene drüber (d.h. schon im Spiel) mit der Lösung dieses Problems ansetzen: Eigentlich ist diese Figurenumwandlung ja komplett zum Spiel gehörig...bei einer KI (ohne GUI) müßte ja auch eine Figurumwandlung stattfinden... Die Auswahl, welche Figur man wählt, ist im Prinzip larifari - es gibt throretisch den Fall, dass man durch die Umwandlung in einen Springer ein Schachmatt erreichen kann, aber ansonsten ist die Auswahl (Dame) ja klar.... Ob das dann über einen Game-Listener gelöst werden sollte? Prinzipiell könnte man da jetzt wieder "rum-Engineeren", aber ... das ist eigentlich dein Job 😉 Ich stelle nur mal in den Raum, ob nicht sowas vielleicht sinnvoller wäre:
Java:
interface PawnReplacementSelector
{
    public Piece getReplacingPiece(Pawn pawn);
}

class SimpleGame
{
    void inGame()
    {
        ...
        if (pawnHasReachedOppositeLine(pawn))
        {
            replacePiece(pawn, pawnReplacementSelectorOfActivePlayer.getReplacingPiece(pawn));
        }
        ...
    }


class HumanPawnReplacementSelector implements PawnReplacementSelector
{
    public Piece getReplacingPiece(Pawn pawn)
    {
        return ... // mit modalem Dialog ausgewähltes piece....
    }
}


class AIPawnReplacementSelector implements PawnReplacementSelector
{
    public Piece getReplacingPiece(Pawn pawn)
    {
        if (canWinWhenReturningKnight()) return someKnight;
        else return someQueen;
    }
}

Die Spieler bekommen dann jeweils den HumanPawnReplacementSelector / AIPawnReplacementSelector zugewiesen, oder implementieren das Interface jeweils selbst auf die angedeutete Weise...

Ich meine nur, dass für die Steuerung des komplett "Game-Inheränten Spielablauf" eigentlich keine Events verwendet werden müßten (oder sollten?) ... muss man sich mal überlegen...
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben