← Back to list

Java Scenario-Based Interview Question 3: Solving the Mars Rover Kata with SOLID Design Principles…

The Mars Rover Kata is a classic coding exercise that challenges developers to think critically about design, modularity, and…

Raju Methuku · 2025-02-23 05:57 · 2 claps · 4.2 min read
#code-kata #solid-principles #solid-principles-in-java #design-pattern-in-java #best-practices
Open on Medium ↗
Wiki topics: 💻 · Programming 🔭 · Astronomy & Space 💪 · Fitness & Wellness

Java Scenario-Based Interview Question 3: Solving the Mars Rover Kata with SOLID Design Principles and Design Patterns.

The Mars Rover Kata is a classic coding exercise that challenges developers to think critically about design, modularity, and extensibility. In this post, we’ll explore a refactored solution to the problem, focusing on how SOLID principles and design patterns can be applied to create clean, maintainable, and extensible code.

By the end of this post, you’ll have a clear understanding of:

  1. The problem statement and requirements.
  2. How the solution is structured and implemented.
  3. The SOLID principles and design patterns used.
  4. Why these design choices make the code robust and extensible.

Problem Statement

The Mars Rover Kata simulates the movement of a rover on a grid. The rover:

  1. Starts at a specific position (x, y) and faces one of the four cardinal directions (N, E, S, W).
  2. Executes a sequence of commands:
  • F: Move forward.
  • B: Move backward.
  • L: Turn left.
  • R: Turn right.
  1. Operates on two types of grids:
  • Torus Grid: Wraps around edges like a donut.
  • Polar Grid: Simulates a spherical surface.
  1. Detects obstacles and halts further commands upon encountering one.

You can find problem statement here: https://kata-log.rocks/mars-rover-kata

You can find code here: https://github.com/raju4789/code-katas/blob/main/src/main/java/com/raju/codekatas/marsrover/refactor/MarsRoverRefactored.java

Code Walkthrough

1. High-Level Architecture

The solution is designed with modularity and extensibility in mind. Here’s an overview of the key components:

  • MarsRoverRefactored: The main class that orchestrates the rover’s behavior.
  • Commands: Encapsulate actions like moving forward or turning.
  • Direction: Represents the rover’s orientation (N, E, S, W).
  • Movement Strategies: Handle grid-specific movement logic (e.g., Torus or Polar grids).
  • Validators: Ensure movements are valid (e.g., no obstacles).
  • Factories: Dynamically create commands and directions.

2. Core Classes

MarsRoverRefactored

This is the central class that coordinates the rover’s behavior. It uses:

  • A MovementStrategy for grid-specific logic.
  • A MovementValidator to check for obstacles.
  • A CommandFactory to map commands to actions.

Code:

public class MarsRoverRefactored {
    private final MovementValidator movementValidator;
    private final MovementStrategy movementStrategy;
    private final int stepLength;
    private Coordinate position;
    private Direction direction;
    private boolean obstacleEncountered = false;

    public MarsRoverRefactored(MovementValidator movementValidator, MovementStrategy movementStrategy, Coordinate initialPosition, String initialDirection, int stepLength) {
        this.movementValidator = movementValidator;
        this.movementStrategy = movementStrategy;
        this.position = initialPosition;
        this.direction = DirectionFactory.getDirection(initialDirection);
        this.stepLength = stepLength;
    }
    public void move(String commands) {
        if (commands == null || commands.isEmpty()) {
            throw new InvalidCommandException("Commands cannot be null or empty");
        }
        CommandFactory commandFactory = new CommandFactory(this);
        for (char commandChar : commands.toCharArray()) {
            if (obstacleEncountered) break;
            try {
                RoverCommand command = commandFactory.getCommand(commandChar);
                command.execute();
            } catch (ObstacleException e) {
                obstacleEncountered = true;
                break;
            }
        }
    }
}

Commands

Each command (MoveForwardCommand, TurnLeftCommand, etc.) implements the RoverCommand interface. This adheres to the Command Pattern, making it easy to add new commands.

Code:

public interface RoverCommand {
    void execute();
}

public class MoveForwardCommand implements RoverCommand {
    private final MarsRoverRefactored rover;
    public MoveForwardCommand(MarsRoverRefactored rover) {
        this.rover = rover;
    }
    @Override
    public void execute() {
        Coordinate newPosition = rover.getMovementStrategy().moveForward(
            rover.getPosition(), rover.getDirection(), rover.getStepLength()
        );
        if (!rover.getMovementValidator().isMovementValid(newPosition)) {
            throw new ObstacleException("Obstacle detected at " + newPosition);
        }
        rover.setPosition(newPosition);
    }
}

Direction

The Direction interface and its implementations (North, East, etc.) encapsulate turning logic. The Factory Pattern is used to create direction objects dynamically.

Code:

public interface Direction {
    Direction turnLeft();
    Direction turnRight();
    DirectionEnum getDirection();
}

public class North implements Direction {
    @Override
    public Direction turnLeft() {
        return new West();
    }
    @Override
    public Direction turnRight() {
        return new East();
    }
    @Override
    public DirectionEnum getDirection() {
        return DirectionEnum.NORTH;
    }
}

Movement Strategies

Movement strategies handle grid-specific logic. For example:

  • TorusMovementStrategy wraps coordinates around grid edges.
  • PolarMovementStrategy adjusts coordinates for a spherical grid.

Code:

public class TorusMovementStrategy implements MovementStrategy {

    @Override
    public Coordinate moveForward(Coordinate currentPosition, Direction currentDirection, int stepSize) {
        return calculateNewPosition(currentPosition, currentDirection, stepSize);
    }

    @Override
    public Coordinate moveBackward(Coordinate currentPosition, Direction currentDirection, int stepSize) {
        return calculateNewPosition(currentPosition, currentDirection, -stepSize);
    }

    private Coordinate calculateNewPosition(Coordinate currentPosition, Direction currentDirection, int stepSize) {
        int newX = currentPosition.getX();
        int newY = currentPosition.getY();

        switch (currentDirection.getDirection()) {
            case NORTH:
                newY = wrapCoordinate(newY + stepSize, MAX_Y);
                break;
            case EAST:
                newX = wrapCoordinate(newX + stepSize, MAX_X);
                break;
            case SOUTH:
                newY = wrapCoordinate(newY - stepSize, MAX_Y);
                break;
            case WEST:
                newX = wrapCoordinate(newX - stepSize, MAX_X);
                break;
            default:
                throw new InvalidCommandException("Invalid direction: " + currentDirection.getDirection());
        }

        return new Coordinate(newX, newY);
    }

    private int wrapCoordinate(int value, int max) {
        return (value % max + max) % max; // Ensures the value wraps correctly for both positive and negative values
    }

Validators

The ObstacleMovementValidator checks if a movement is valid by consulting the ObstacleDetector.

Code:

public class ObstacleMovementValidator implements MovementValidator {
    private final ObstacleDetector obstacleDetector;
    public ObstacleMovementValidator(ObstacleDetector obstacleDetector) {
        this.obstacleDetector = obstacleDetector;
    }
    @Override
    public boolean isMovementValid(Coordinate coordinate) {
        return !obstacleDetector.isObstacle(coordinate);
    }
}

Applying SOLID Principles

1. Single Responsibility Principle (SRP)

Each class has a single responsibility:

  • CommandFactory maps commands to actions.
  • ObstacleDetector detects obstacles.
  • TorusMovementStrategy handles toroidal grid logic.

2. Open/Closed Principle (OCP)

The system is open for extension but closed for modification. For example:

  • Adding a new command (e.g., diagonal movement) only requires creating a new RoverCommand implementation.
  • Adding a new grid type only requires implementing a new MovementStrategy.

3. Liskov Substitution Principle (LSP)

All Direction and MovementStrategy implementations can be used interchangeably without altering the behavior of MarsRoverRefactored.

4. Interface Segregation Principle (ISP)

Interfaces like RoverCommand and MovementStrategy are small and focused, ensuring classes only implement what they need.

5. Dependency Inversion Principle (DIP)

High-level modules (MarsRoverRefactored) depend on abstractions (MovementStrategy, MovementValidator), not concrete implementations.

Design Patterns in Action

1. Command Pattern

Encapsulates commands as objects, making the system extensible and decoupled.

2. Factory Pattern

Used in CommandFactory and DirectionFactory to create objects dynamically.

3. Strategy Pattern

Encapsulates movement logic in TorusMovementStrategy and PolarMovementStrategy.

Conclusion

The Mars Rover Kata is a great exercise for practicing clean code principles and design patterns. By adhering to SOLID principles and leveraging patterns like Command and Strategy, we created a solution that is functional, extensible, and maintainable.

👋 Let’s Connect!

If you found this post insightful, here’s how you can help spread the knowledge:

👏 Clap if you enjoyed it — your claps motivate me to keep sharing more Java tricks and insights! 🔗 Share this post with your network so others can learn too. 💬 Ask your questions or share your experiences in the comments. Have you encountered this situation? Let’s discuss!

🚀 Follow me for more deep dives into Java, design patterns, and programming gotchas! Let’s learn and grow together. 🌟


메타데이터
post_id
08a498fb0fbe
slug
a-step-by-step-guide-to-solving-the-mars-rover-kata-with-solid-design-principles-and-design-08a498fb0fbe
url
https://medium.com/@narasimha4789/a-step-by-step-guide-to-solving-the-mars-rover-kata-with-solid-design-principles-and-design-08a498fb0fbe
canonical_url
https://medium.com/@narasimha4789/a-step-by-step-guide-to-solving-the-mars-rover-kata-with-solid-design-principles-and-design-08a498fb0fbe
author_url
https://medium.com/@narasimha4789
status
ok
fetched_at
2026-08-06 07:53:32