Home > Blockchain >  How to implement undo function without violating encapsulation?
How to implement undo function without violating encapsulation?

Time:10-24

Lets image a simple board game:

  • Every turn you can place one figure.
  • You must not remove a figure.
  • When a certain condition is met, the game is over.

Now I want to implement an undo function. I have to following classes:

Game {
  (...)
    placeFigure(int,int): void
    isGameOver(): bool
}

Move {
  (...)
    Move(Game,int,int)
    execute(): void
    undo(): void
}

History {
  - moves: List<Move>
  (...)
    add(Move): void
    undo(): void
}

My problem is, in order for a move to be undone, I have to add a function removeFigure. But, calling this function improperly would result in an invalid state. The other idea I hade was to make Move a nested class of Game. This however, will bloat the Game class and make it tedious to add other possible moves in the future.

Should I implement one of those ideas? Perhaps there is a simpler solution to this problem?

CodePudding user response:

Without knowing the exact objectives of your game nor what other types of moves you had in mind (I assumed moves that also place figures), here's the approach I, in java, would probably take based on the current constraints you've put forth.

/* Game.java */
import java.util.Stack;

public class Game
{
  protected static abstract class Move
  {
    protected final int x;
    protected final int y;

    protected Move(int x, int y) {
      this.x = x;
      this.y = y;
    }

    protected void execute(Game game) {
      game.placeFigure(x, y);
    }

    protected void undo(Game game) {
      game.removeFigure(x, y);
    }
  }

  private Stack<Move> history = new Stack<Move>();

  public void doMove(Move move) {
    move.execute(this);
    history.push(move);
  }

  public void undoMove() {
    if(history.empty()) {
      System.out.println("no move to undo");
    }
    else {
      Move move = history.pop();
      move.undo(this);
    }
  }

  protected void placeFigure(int x, int y) {
    System.out.println("place figure at: "   x   ","   y);
  }

  protected void removeFigure(int x, int y) {
    System.out.println("remove figure at: "   x   ","   y);
  }
}

/* DefaultMove.java */
public class DefaultMove extends Game.Move
{
  public DefaultMove(int x, int y) {
    super(x, y);
  }
}

/* SpecialMove.java */
public class SpecialMove extends Game.Move
{
  public SpecialMove(int x, int y) {
    super(x   10, y   10);
    System.out.println("SpecialMove displaces x and y by 10");
  }
}

/* Main.java */
public class Main
{
  public static void main(String[] arguments) {
    Game game = new Game();
    DefaultMove defaultMove = new DefaultMove(3,6);
    SpecialMove specialMove = new SpecialMove(4,5);
    game.doMove(defaultMove);
    game.doMove(specialMove);
    game.undoMove();
    game.undoMove();
    game.undoMove();
  }
}

Which prints:

SpecialMove displaces x and y by 10
place figure at: 3,6
place figure at: 14,15
remove figure at: 14,15
remove figure at: 3,6
no move to undo

Maybe it's not at all what you are looking for, but hopefully this gives you some ideas to work with. Let me know if you had completely other objectives in mind and I'll see if I can accommodate.

  • Related