Predict mars robot position Description


  A robot lands on Mars, which happens to be a cartesian grid; assuming that we hand the robot these instructions, such as LFFFRFFFRRFFF, where "L" is a "turn 90 degrees left", "R" is a "turn 90 degrees right", and "F" is "go forward one space, please write control code for the robot such that it ends up at the appropriate-and-correct destination, and include unit tests.


Here is an example output with command "FF":

[0, 2]

Code

class Robot {
  private int x;
  private int y;
  private int currentDirection;

  Robot() {
    this(0, 0);
  }

  Robot(int x, int y) {
    this.x = x;
    this.y = y;
    currentDirection = 0;
  }

  public void move(String moves) {
    for (char ch : moves.toCharArray()) {
      if (ch == 'R') currentDirection += 1;
      if (ch == 'L') currentDirection -= 1;

      currentDirection = currentDirection % 4;

      if (ch != 'F') continue;

      System.out.println(currentDirection);
      if (currentDirection == 0) {
        y += 1;
      } if (currentDirection == 1) {
        x += 1;
      } if (currentDirection == 2) {
        y -= 1;
      } if (currentDirection == 3) {
        x -= 1;
      }
    }
  }

  public void reset() {
    x = 0;
    y = 0;
    currentDirection = 0;
  }

  public String position() {
    return x + ":" + y;
  }
}

class Main {
  public static void main(String[] args) {
    Robot robot = new Robot();
    robot.move("FF");
    System.out.println(robot.position()); // 0,2
    robot.reset();
    System.out.println(robot.position()); // 0,0
    robot.move("FFRF");
    System.out.println(robot.position()); // 1,2
    robot.reset();
    robot.move("FFRRRFF");
    System.out.println(robot.position()); // -2,2
  }
}


How can I make this code more object oriented?
I would consider refactoring direction into an enum, instead of a magic number. This would allow to encapsulate the related logic in this type.

It might look like that:

    enum Direction {
        UP(0, 1),
        RIGHT(1, 0),
        DOWN(0, -1),
        LEFT(-1, 0);

        final int xVector;
        final int yVector;

        private final Direction FIRST = Direction.values()[0];
        private final Direction LAST = Direction.values()[Direction.values().length];

        Direction(int xVector, int yVector) {
            this.xVector = xVector;
            this.yVector = yVector;
        }

        Direction rotatedLeft() {
            return this == FIRST
                    ? LAST // cycle complete
                    : Direction.values()[ordinal() - 1]; // previous value
        }

        Direction rotatedRight() {
            return this == LAST
                    ? FIRST // cycle complete
                    : Direction.values()[ordinal() + 1]; // next value
        }
    }


And then the Robot code becomes (including some subjective clean up, unrelated to the object-oriented aspect itself):

class Robot {
    private int x;
    private int y;
    private Direction currentDirection;

    Robot() {
        this(0, 0);
    }

    Robot(int x, int y) {
        this.x = x;
        this.y = y;
        currentDirection = Direction.UP;
    }

    public void move(String moves) {
        for (char code : moves.toCharArray()) {
            // a matter of taste, I like to extract logic 
            // out of looping constructs for clarity
            move(code);
        }
    }

    private void move(char code) {
        switch (code) {
            case 'R': {
                currentDirection = currentDirection.rotatedRight();
                break;
            }
            case 'L': {
                currentDirection = currentDirection.rotatedLeft();
                break;
            }
            case 'F': {
                // you'd call the println thing here.
                // as it's not part of the requirements I assumed it to be a debugging artifact
                this.x += currentDirection.xVector;
                this.y += currentDirection.yVector;
                break;
            }
        }
    }

    public void reset() {
        x = 0;
        y = 0;
        currentDirection = Direction.UP;
    }

    public String position() {
        return x + ":" + y;
    }
}


I think this is more object-oriented, and also an improvement. 

You could take it one step further and encapsulate the x and y coordinates in a little standalone class (Position). Like so:

    class Position {
        static final Position DEFAULT = new Position(0, 0);

        final int x;
        final int y;

        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Position movedInto(Direction direction) {
            return new Position(x + direction.xVector, y + direction.yVector);
        }

        @Override
        public String toString() {
            return x + ":" + y;
        }
    }


Then in the Robot class you replace x and y fields with Position position, and you no longer recalculate the position in the Robot class, as you can simply go:

case 'F': {
    position = position.movedInto(currentDirection);
    break;
}


in the move method.

Plus:

    public void reset() {
        position = Position.DEFAULT;
        currentDirection = Direction.UP;
    }

    public String position() {
        return position.toString();
    }


Yet another idea would be to encapsulate the move codes themselves into a class or classes. It's sometimes hard to say where to draw the line, as making code more and more OO comes at the cost of more boilerplate, and can be a form of overengineering.

I admit I haven't tested my refactored version, I only approached the question in terms of code design.