1
0
Fork 0

sacrify type safety to obtain independent deployability when a command is added (now the api module doesn't need to be recompiled+redeployed)

This commit is contained in:
Xavier Fontanet 2024-07-05 17:17:30 +02:00
parent 42f1f19926
commit 8860bfcbc0
14 changed files with 78 additions and 50 deletions

View File

@ -0,0 +1,7 @@
package cat.hack3.codingtests.marsrover.api;
import cat.hack3.codingtests.marsrover.api.cartography.Direction;
public interface AccelerometeredRover extends Rover{
Direction getCurrentDirection();
}

View File

@ -0,0 +1,4 @@
package cat.hack3.codingtests.marsrover.api;
public interface ConsciousRover extends AccelerometeredRover, LocateableRover{
}

View File

@ -2,6 +2,6 @@ package cat.hack3.codingtests.marsrover.api;
import cat.hack3.codingtests.marsrover.api.cartography.Coordinates;
public interface LocateableRover {
public interface LocateableRover extends Rover{
Coordinates getCurrentCoordinates();
}

View File

@ -2,7 +2,6 @@ package cat.hack3.codingtests.marsrover.api;
import cat.hack3.codingtests.marsrover.api.cartography.Direction;
public interface RiderRover {
public interface RiderRover extends AccelerometeredRover{
void moveTowards(Direction direction);
Direction getCurrentDirection();
}

View File

@ -3,7 +3,7 @@ package cat.hack3.codingtests.marsrover.api;
import cat.hack3.codingtests.marsrover.api.cartography.Direction;
import cat.hack3.codingtests.marsrover.api.cartography.PlanetMap;
public interface RotableRiderRover extends RotableRover, RiderRover, LocateableRover {
public interface RotableRiderRover extends RotableRover, RiderRover, ConsciousRover {
interface Provider {
RotableRiderRover provideWith(PlanetMap map, Direction startingDirection);

View File

@ -1,6 +1,6 @@
package cat.hack3.codingtests.marsrover.api;
public interface RotableRover {
public interface RotableRover extends Rover{
enum Rotation {LEFT, RIGHT}
void rotateTowards(Rotation rotation);

View File

@ -0,0 +1,4 @@
package cat.hack3.codingtests.marsrover.api;
public interface Rover {
}

View File

@ -1,7 +1,6 @@
package cat.hack3.codingtests.marsrover.api.commands;
public interface RoverCommand {
enum Type {MOVE_FORWARD, MOVE_BACKWARDS, TURN_LEFT, TURN_RIGHT}
void execute();
}

View File

@ -1,11 +1,13 @@
package cat.hack3.codingtests.marsrover.api.commands;
import cat.hack3.codingtests.marsrover.api.RotableRiderRover;
import cat.hack3.codingtests.marsrover.api.Rover;
public interface RoverCommandFactory {
RoverCommand create(RoverCommand.Type type);
RoverCommand create(String commandName) throws InvalidCommandNameProvided;
interface Provider {
RoverCommandFactory provideWith(RotableRiderRover rover);
RoverCommandFactory provideWith(Rover rover);
}
class InvalidCommandNameProvided extends RuntimeException{}
}

View File

@ -0,0 +1,45 @@
package cat.hack3.codingtests.marsrover.commands;
import cat.hack3.codingtests.marsrover.api.RotableRiderRover;
import cat.hack3.codingtests.marsrover.api.Rover;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommand;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommandFactory;
import java.util.Optional;
public class RotableRiderRoverCommandFactory implements RoverCommandFactory {
enum RoverCommandType {MOVE_FORWARD, MOVE_BACKWARDS, TURN_LEFT, TURN_RIGHT}
private final RotableRiderRover rover;
public static class Provider implements RoverCommandFactory.Provider {
@Override
public RoverCommandFactory provideWith(Rover rover) {
return new RotableRiderRoverCommandFactory((RotableRiderRover) rover);
}
}
private RotableRiderRoverCommandFactory(RotableRiderRover rover) {
this.rover = rover;
}
@Override
public RoverCommand create(String commandName) {
var commandType = tryResolvingCommandTypeFrom(commandName)
.orElseThrow(InvalidCommandNameProvided::new);
return switch (commandType) {
case MOVE_FORWARD -> new MoveForwardCommand(rover);
case MOVE_BACKWARDS -> new MoveBackwardsCommand(rover);
case TURN_LEFT -> new TurnLeftCommand(rover);
case TURN_RIGHT -> new TurnRightCommand(rover);
};
}
private Optional<RoverCommandType> tryResolvingCommandTypeFrom(String commandName) {
try {
return Optional.of(RoverCommandType.valueOf(commandName));
} catch (IllegalArgumentException e) {
return Optional.empty();
}
}
}

View File

@ -1,31 +0,0 @@
package cat.hack3.codingtests.marsrover.commands;
import cat.hack3.codingtests.marsrover.api.RotableRiderRover;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommand;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommandFactory;
public class RoverCommandFactoryImpl implements RoverCommandFactory {
private final RotableRiderRover rover;
public static class Provider implements RoverCommandFactory.Provider {
@Override
public RoverCommandFactory provideWith(RotableRiderRover rover) {
return new RoverCommandFactoryImpl(rover);
}
}
private RoverCommandFactoryImpl(RotableRiderRover rover) {
this.rover = rover;
}
@Override
public RoverCommand create(RoverCommand.Type type) {
return switch (type) {
case MOVE_FORWARD -> new MoveForwardCommand(rover);
case MOVE_BACKWARDS -> new MoveBackwardsCommand(rover);
case TURN_LEFT -> new TurnLeftCommand(rover);
case TURN_RIGHT -> new TurnRightCommand(rover);
};
}
}

View File

@ -1,9 +1,9 @@
import cat.hack3.codingtests.marsrover.api.commands.RoverCommandFactory;
import cat.hack3.codingtests.marsrover.commands.RoverCommandFactoryImpl;
import cat.hack3.codingtests.marsrover.commands.RotableRiderRoverCommandFactory;
module rover.commands {
requires rover.api;
provides RoverCommandFactory.Provider
with RoverCommandFactoryImpl.Provider;
with RotableRiderRoverCommandFactory.Provider;
}

View File

@ -1,5 +1,6 @@
package cat.hack3.codingtests.marsrover.test.context;
import cat.hack3.codingtests.marsrover.api.ConsciousRover;
import cat.hack3.codingtests.marsrover.api.RotableRiderRover;
import cat.hack3.codingtests.marsrover.api.cartography.Direction;
@ -8,7 +9,7 @@ import static cat.hack3.codingtests.marsrover.api.cartography.Direction.SOUTH;
public abstract class GivenMarsRoverDeployedOnMarsMap extends GivenMarsMapSquaredWithInitialCoordinates{
static final Direction STARTING_DIRECTION = SOUTH;
protected RotableRiderRover rover;
protected ConsciousRover rover;
public void setUp() {
super.setUp();

View File

@ -3,8 +3,6 @@ package cat.hack3.codingtests.marsrover.test.context;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommand;
import cat.hack3.codingtests.marsrover.api.commands.RoverCommandFactory;
import static cat.hack3.codingtests.marsrover.api.commands.RoverCommand.Type.*;
public class GivenRotableAndMoveCommandsOnDeployedRoverWithMap extends GivenMarsRoverDeployedOnMarsMap {
public static final Class<RoverCommandFactory.Provider> COMMAND_FACTORY_PROVIDER = RoverCommandFactory.Provider.class;
@ -15,12 +13,12 @@ public class GivenRotableAndMoveCommandsOnDeployedRoverWithMap extends GivenMars
public void setUp() {
super.setUp();
var commandFactory = getImplProviderOf(COMMAND_FACTORY_PROVIDER)
RoverCommandFactory commandFactory = getImplProviderOf(COMMAND_FACTORY_PROVIDER)
.provideWith(rover);
moveForwardCommand = commandFactory.create(MOVE_FORWARD);
moveBackwardsCommand = commandFactory.create(MOVE_BACKWARDS);
turnLeftCommand = commandFactory.create(TURN_LEFT);
turnRightCommand = commandFactory.create(TURN_RIGHT);
moveForwardCommand = commandFactory.create("MOVE_FORWARD");
moveBackwardsCommand = commandFactory.create("MOVE_BACKWARDS");
turnLeftCommand = commandFactory.create("TURN_LEFT");
turnRightCommand = commandFactory.create("TURN_RIGHT");
}
}