├── README.md └── katas_java ├── .gitignore ├── 01-mars-rover-movement ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── .keep │ └── test │ └── java │ └── RoverTest.java ├── 02_smelly-mars-rover-refactoring ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── mars_rover │ │ └── Rover.java │ └── test │ └── java │ ├── RoverEqualityTests.java │ ├── RoverPositionTests.java │ ├── RoverReceivingCommandsListTests.java │ └── RoverRotationTests.java ├── 03a-mars-rover-adding-new-feature ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── mars_rover │ │ ├── Coordinates.java │ │ ├── Direction.java │ │ └── MarsRover.java │ └── test │ └── java │ └── MarsRoverTest.java ├── 03b-mars-rover-adding-new-feature ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── mars_rover │ │ ├── Command.java │ │ ├── MarsRover.java │ │ ├── NasaCommunicationProtocol.java │ │ ├── command_types │ │ ├── MovingBackwards.java │ │ ├── MovingForward.java │ │ ├── TurningLeft.java │ │ ├── TurningRight.java │ │ └── UnknownCommand.java │ │ └── location │ │ ├── Coordinates.java │ │ ├── Direction.java │ │ └── Vector.java │ └── test │ └── java │ └── MarsRoverTest.java ├── 04-working-with-code-we-do-not-own ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── items │ │ └── Item.java │ └── test │ └── java │ └── GildedRoseTest.java ├── 05-refactoring-awful-inheritance-use-with-beverage-prices-kata ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── beverages │ │ ├── Beverage.java │ │ ├── Coffee.java │ │ ├── CoffeeWithMilk.java │ │ ├── CoffeeWithMilkAndCream.java │ │ ├── HotChocolate.java │ │ ├── HotChocolateWithCream.java │ │ ├── Tea.java │ │ └── TeaWithMilk.java │ └── test │ └── java │ └── unit_tests │ └── BeveragesPricingTest.java ├── 06-refactoring-to-hexagonal-architecture ├── pom.xml ├── readme.md └── src │ ├── main │ └── java │ │ └── birthdaygreetings │ │ ├── BirthdayService.java │ │ ├── Employee.java │ │ └── OurDate.java │ └── test │ ├── java │ └── birthdaygreetings │ │ └── test │ │ ├── AcceptanceTest.java │ │ ├── EmployeeTest.java │ │ └── OurDateTest.java │ └── resources │ └── employee_data.txt ├── 07-refactoring-to-hexagonal-architecture-2 ├── pom.xml ├── readme.md └── src │ ├── main │ └── java │ │ └── birthdaygreetings │ │ ├── application │ │ ├── BirthdayService.java │ │ └── Main.java │ │ ├── core │ │ ├── CannotReadEmployeesException.java │ │ ├── Employee.java │ │ ├── EmployeesRepository.java │ │ ├── Greeting.java │ │ ├── GreetingMessage.java │ │ └── OurDate.java │ │ └── infrastructure │ │ └── repositories │ │ ├── DateRepresentation.java │ │ ├── EmployeesFile.java │ │ └── FileEmployeesRepository.java │ └── test │ ├── java │ └── test │ │ └── birthdaygreetings │ │ ├── application │ │ └── BirthdayServiceAcceptanceTest.java │ │ ├── core │ │ ├── EmployeeTest.java │ │ └── OurDateTest.java │ │ ├── helpers │ │ └── OurDateFactory.java │ │ └── infrastructure │ │ └── repositories │ │ └── FileEmployeeRepositoryTest.java │ └── resources │ ├── employee_data.txt │ ├── wrong_data__one-field-only.csv │ └── wrong_data__wrong-date-format.csv ├── 08-doing-tdd-on-legacy-code ├── README.md ├── pom.xml ├── res │ ├── example.html │ └── example.txt └── src │ ├── main │ └── java │ │ └── text_converter │ │ └── HtmlTextConverter.java │ └── test │ └── java │ └── text_converter │ └── .keepme ├── 09-outside-in-with-ohce-kata ├── part_1 │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ └── java │ │ │ └── ohce │ │ │ └── .keep │ │ └── test │ │ └── java │ │ └── test │ │ └── ohce │ │ └── .keep ├── part_2 │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ └── java │ │ │ └── ohce │ │ │ └── .keep │ │ └── test │ │ └── java │ │ └── test │ │ └── ohce │ │ └── .keep └── readme.md ├── 10-outside-in-with-bank-kata ├── .gitignore ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── bank │ │ └── .keep │ └── test │ └── java │ └── bank │ └── tests │ ├── acceptance │ └── PrintingAccountStatementOnConsoleTest.java │ └── unit │ └── .keep └── 11-outside-in-with-unusual-spending-alert-service ├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── alert_service │ └── .keep └── test └── java └── alert_service └── tests ├── acceptance └── AlertingUsersWithUnusualSpendingsTest.java └── unit └── .keep /README.md: -------------------------------------------------------------------------------- 1 | # Deliberate Practice Program 2 | 3 | Practice program to build up code skills through katas, readings and talks. 4 | 5 | Run before at: 6 | 7 | * Magento, Merkle, Trovit, Mango, Properati and Genially. 8 | 9 | Currently being run at: 10 | 11 | * Audiense and Aida. 12 | -------------------------------------------------------------------------------- /katas_java/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | .classpath 3 | .project 4 | .idea 5 | *.iml 6 | target -------------------------------------------------------------------------------- /katas_java/01-mars-rover-movement/README.md: -------------------------------------------------------------------------------- 1 | # Mars Rover implementation exercise 2 | 3 | ### Description 4 | In this exercise we'll implement from scratch part of the Mars Rover kata. 5 | 6 | This is a summary of the behavior of the rover that we'll implement: 7 | 8 | - It's located on a grid at some point with coordinates (x,y) and facing a direction encoded with a character. 9 | 10 | - The meaning of each direction character is: 11 | 12 | * ``N`` -> North 13 | * ``S`` -> South 14 | * ``E`` -> East 15 | * ``W`` -> West 16 | 17 | - The rover receives a sequence of commands (a string of characters) which are codified in the following way: 18 | 19 | * When it receives an ``f``, it moves forward one position in the direction it is facing. 20 | * When it receives a ``b``, it moves backward one position in the direction it is facing. 21 | * When it receives a ``l``, it turns left changing its direction (by one step in the direction). 22 | * When it receives a ``r``, it turns right changing its direction (by one step in the direction). 23 | 24 | -------------------------------------------------------------------------------- /katas_java/01-mars-rover-movement/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | mars-rover-movement-tdd 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /katas_java/01-mars-rover-movement/src/main/java/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/01-mars-rover-movement/src/main/java/.keep -------------------------------------------------------------------------------- /katas_java/01-mars-rover-movement/src/test/java/RoverTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | 6 | public class RoverTest { 7 | 8 | @Test 9 | public void fix_and_rename_me() { 10 | assertThat(true, is(false)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/README.md: -------------------------------------------------------------------------------- 1 | Smelly Mars Rover code refactoring 2 | ============================================= 3 | 4 | Smelly Mars Rover code to practice refactoring. 5 | 6 | We'll use it to learn to recognize some code smells 7 | and to fix them applying some useful refactorings. -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | smelly-mars-rover-refactoring 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/src/main/java/mars_rover/Rover.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | public class Rover { 4 | 5 | private String direction; 6 | private int y; 7 | private int x; 8 | 9 | public Rover(int x, int y, String direction) { 10 | this.direction = direction; 11 | this.y = y; 12 | this.x = x; 13 | } 14 | 15 | public void receive(String commandsSequence) { 16 | for (int i = 0; i < commandsSequence.length(); ++i) { 17 | String command = commandsSequence.substring(i, i + 1); 18 | 19 | if (command.equals("l") || command.equals("r")) { 20 | 21 | // Rotate Rover 22 | if (direction.equals("N")) { 23 | if (command.equals("r")) { 24 | direction = "E"; 25 | } else { 26 | direction = "W"; 27 | } 28 | } else if (direction.equals("S")) { 29 | if (command.equals("r")) { 30 | direction = "W"; 31 | } else { 32 | direction = "E"; 33 | } 34 | } else if (direction.equals("W")) { 35 | if (command.equals("r")) { 36 | direction = "N"; 37 | } else { 38 | direction = "S"; 39 | } 40 | } else { 41 | if (command.equals("r")) { 42 | direction = "S"; 43 | } else { 44 | direction = "N"; 45 | } 46 | } 47 | } else { 48 | 49 | // Displace Rover 50 | int displacement1 = -1; 51 | 52 | if (command.equals("f")) { 53 | displacement1 = 1; 54 | } 55 | int displacement = displacement1; 56 | 57 | if (direction.equals("N")) { 58 | y += displacement; 59 | } else if (direction.equals("S")) { 60 | y -= displacement; 61 | } else if (direction.equals("W")) { 62 | x -= displacement; 63 | } else { 64 | x += displacement; 65 | } 66 | } 67 | } 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) return true; 73 | if (o == null || getClass() != o.getClass()) return false; 74 | 75 | Rover rover = (Rover) o; 76 | 77 | if (y != rover.y) return false; 78 | if (x != rover.x) return false; 79 | return direction != null ? direction.equals(rover.direction) : rover.direction == null; 80 | 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int result = direction != null ? direction.hashCode() : 0; 86 | result = 31 * result + y; 87 | result = 31 * result + x; 88 | return result; 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "Rover{" + 94 | "direction='" + direction + '\'' + 95 | ", y=" + y + 96 | ", x=" + x + 97 | '}'; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverEqualityTests.java: -------------------------------------------------------------------------------- 1 | import static org.junit.Assert.*; 2 | 3 | import mars_rover.Rover; 4 | import org.junit.Test; 5 | 6 | public class RoverEqualityTests { 7 | @Test 8 | public void equalRovers() { 9 | assertEquals(new Rover(1, 1, "N"), new Rover(1, 1, "N")); 10 | } 11 | 12 | @Test 13 | public void notEqualRovers() { 14 | assertFalse(new Rover(1, 1, "N").equals(new Rover(1, 1, "S"))); 15 | assertFalse(new Rover(1, 1, "N").equals(new Rover(1, 2, "N"))); 16 | assertFalse(new Rover(1, 1, "N").equals(new Rover(0, 1, "N"))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverPositionTests.java: -------------------------------------------------------------------------------- 1 | import static org.junit.Assert.*; 2 | 3 | import mars_rover.Rover; 4 | import org.junit.Test; 5 | 6 | public class RoverPositionTests { 7 | @Test 8 | public void facingNorthMoveForward() { 9 | Rover rover = new Rover(0, 0, "N"); 10 | 11 | rover.receive("f"); 12 | 13 | assertEquals(new Rover(0, 1, "N"), rover); 14 | } 15 | 16 | @Test 17 | public void facingNorthMoveBackward() { 18 | Rover rover = new Rover(0, 0, "N"); 19 | 20 | rover.receive("b"); 21 | 22 | assertEquals(new Rover(0, -1, "N"), rover); 23 | } 24 | 25 | @Test 26 | public void facingSouthMoveForward() { 27 | Rover rover = new Rover(0, 0, "S"); 28 | 29 | rover.receive("f"); 30 | 31 | assertEquals(new Rover(0, -1, "S"), rover); 32 | } 33 | 34 | @Test 35 | public void facingSouthMoveBackward() { 36 | Rover rover = new Rover(0, 0, "S"); 37 | 38 | rover.receive("b"); 39 | 40 | assertEquals(new Rover(0, 1, "S"), rover); 41 | } 42 | 43 | @Test 44 | public void facingWestMoveForward() { 45 | Rover rover = new Rover(0, 0, "W"); 46 | 47 | rover.receive("f"); 48 | 49 | assertEquals(new Rover(-1, 0, "W"), rover); 50 | } 51 | 52 | @Test 53 | public void facingWestMoveBackward() { 54 | Rover rover = new Rover(0, 0, "W"); 55 | 56 | rover.receive("b"); 57 | 58 | assertEquals(new Rover(1, 0, "W"), rover); 59 | } 60 | 61 | @Test 62 | public void facingEastMoveForward() { 63 | Rover rover = new Rover(0, 0, "E"); 64 | 65 | rover.receive("f"); 66 | 67 | assertEquals(new Rover(1, 0, "E"), rover); 68 | } 69 | 70 | @Test 71 | public void facingEastMoveBackward() { 72 | Rover rover = new Rover(0, 0, "E"); 73 | 74 | rover.receive("b"); 75 | 76 | assertEquals(new Rover(-1, 0, "E"), rover); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverReceivingCommandsListTests.java: -------------------------------------------------------------------------------- 1 | import static org.junit.Assert.assertEquals; 2 | 3 | import mars_rover.Rover; 4 | import org.junit.Test; 5 | 6 | public class RoverReceivingCommandsListTests { 7 | @Test 8 | public void noCommands() { 9 | Rover rover = new Rover(0, 0, "N"); 10 | 11 | rover.receive(""); 12 | 13 | assertEquals(new Rover(0, 0, "N"), rover); 14 | } 15 | 16 | @Test 17 | public void twoCommands() { 18 | Rover rover = new Rover(0, 0, "N"); 19 | 20 | rover.receive("lf"); 21 | 22 | assertEquals(new Rover(-1, 0, "W"), rover); 23 | } 24 | 25 | @Test 26 | public void manyCommands() { 27 | Rover rover = new Rover(0, 0, "N"); 28 | 29 | rover.receive("ffrbbrfflff"); 30 | 31 | assertEquals(new Rover(0, 0, "E"), rover); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverRotationTests.java: -------------------------------------------------------------------------------- 1 | import static org.junit.Assert.*; 2 | 3 | import mars_rover.Rover; 4 | import org.junit.Test; 5 | 6 | public class RoverRotationTests { 7 | @Test 8 | public void facingNorthRotateLeft() { 9 | Rover rover = new Rover(0, 0, "N"); 10 | 11 | rover.receive("l"); 12 | 13 | assertEquals(new Rover(0, 0, "W"), rover); 14 | } 15 | 16 | @Test 17 | public void facingNorthRotateRight() { 18 | Rover rover = new Rover(0, 0, "N"); 19 | 20 | rover.receive("r"); 21 | 22 | assertEquals(new Rover(0, 0, "E"), rover); 23 | } 24 | 25 | @Test 26 | public void facingSouthRotateLeft() { 27 | Rover rover = new Rover(0, 0, "S"); 28 | 29 | rover.receive("l"); 30 | 31 | assertEquals(new Rover(0, 0, "E"), rover); 32 | } 33 | 34 | @Test 35 | public void facingSouthRotateRight() { 36 | Rover rover = new Rover(0, 0, "S"); 37 | 38 | rover.receive("r"); 39 | 40 | assertEquals(new Rover(0, 0, "W"), rover); 41 | } 42 | 43 | @Test 44 | public void facingWestRotateLeft() { 45 | Rover rover = new Rover(0, 0, "W"); 46 | 47 | rover.receive("l"); 48 | 49 | assertEquals(new Rover(0, 0, "S"), rover); 50 | } 51 | 52 | @Test 53 | public void facingWestRotateRight() { 54 | Rover rover = new Rover(0, 0, "W"); 55 | 56 | rover.receive("r"); 57 | 58 | assertEquals(new Rover(0, 0, "N"), rover); 59 | } 60 | 61 | @Test 62 | public void facingEastRotateLeft() { 63 | Rover rover = new Rover(0, 0, "E"); 64 | 65 | rover.receive("l"); 66 | 67 | assertEquals(new Rover(0, 0, "N"), rover); 68 | } 69 | 70 | @Test 71 | public void facingEastRotateRight() { 72 | Rover rover = new Rover(0, 0, "E"); 73 | 74 | rover.receive("r"); 75 | 76 | assertEquals(new Rover(0, 0, "S"), rover); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/README.md: -------------------------------------------------------------------------------- 1 | # Mars Rover: adding a feature to existing code 2 | 3 | We'll practice adding a new feature to an existing code base. 4 | 5 | We'll have to refactor a bit first to make the change easy. -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | mars-rover-adding-new-feature 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/Coordinates.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | class Coordinates { 4 | 5 | private final int x; 6 | private final int y; 7 | 8 | Coordinates(int x, int y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | Coordinates incrementX(int delta) { 14 | return new Coordinates(x + delta, y); 15 | } 16 | 17 | Coordinates incrementY(int delta) { 18 | return new Coordinates(x, y + delta); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "Coordinates{" + 24 | "x=" + x + 25 | ", y=" + y + 26 | '}'; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) return true; 32 | if (o == null || getClass() != o.getClass()) return false; 33 | 34 | Coordinates that = (Coordinates) o; 35 | 36 | if (x != that.x) return false; 37 | return y == that.y; 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int result = x; 43 | result = 31 * result + y; 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/Direction.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | enum Direction { 4 | North { 5 | public Direction turnRight() { 6 | return East; 7 | } 8 | 9 | public Direction turnLeft() { 10 | return West; 11 | } 12 | 13 | public Coordinates moveForward(Coordinates coordinates, int delta) { 14 | return coordinates.incrementY(delta); 15 | } 16 | 17 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 18 | return coordinates.incrementY(-delta); 19 | } 20 | }, 21 | South { 22 | public Direction turnRight() { 23 | return West; 24 | } 25 | 26 | public Direction turnLeft() { 27 | return East; 28 | } 29 | 30 | public Coordinates moveForward(Coordinates coordinates, int delta) { 31 | return coordinates.incrementY(-delta); 32 | } 33 | 34 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 35 | return coordinates.incrementY(delta); 36 | } 37 | }, 38 | East { 39 | public Direction turnRight() { 40 | return South; 41 | } 42 | 43 | public Direction turnLeft() { 44 | return North; 45 | } 46 | 47 | public Coordinates moveForward(Coordinates coordinates, int delta) { 48 | return coordinates.incrementX(delta); 49 | } 50 | 51 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 52 | return coordinates.incrementX(-delta); 53 | } 54 | }, 55 | West { 56 | public Direction turnRight() { 57 | return North; 58 | } 59 | 60 | public Direction turnLeft() { 61 | return South; 62 | } 63 | 64 | public Coordinates moveForward(Coordinates coordinates, int delta) { 65 | return coordinates.incrementX(-delta); 66 | } 67 | 68 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 69 | return coordinates.incrementX(delta); 70 | } 71 | }; 72 | 73 | static public Direction parse(String directionAsString) { 74 | if (directionAsString.equals("N")) { 75 | return North; 76 | } else if (directionAsString.equals("E")) { 77 | return East; 78 | } else if (directionAsString.equals("S")) { 79 | return South; 80 | } else { 81 | return West; 82 | } 83 | } 84 | 85 | abstract public Direction turnRight(); 86 | 87 | abstract public Direction turnLeft(); 88 | 89 | abstract public Coordinates moveForward(Coordinates coordinates, int delta); 90 | 91 | abstract public Coordinates moveBackwards(Coordinates coordinates, int delta); 92 | } 93 | -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/MarsRover.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | public class MarsRover { 4 | 5 | private static final int MOVEMENT_DELTA = 1; 6 | private Coordinates coordinates; 7 | private Direction direction; 8 | 9 | public MarsRover(int x, int y, String direction) { 10 | this.coordinates = new Coordinates(x, y); 11 | this.direction = Direction.parse(direction); 12 | } 13 | 14 | public void receive(String commandsSequence) { 15 | for (String command : commandsSequence.split("")) { 16 | executeCommand(command); 17 | } 18 | } 19 | 20 | private void executeCommand(String command) { 21 | if (command.equals("r")) { 22 | direction = direction.turnRight(); 23 | } else if (command.equals("l")) { 24 | direction = direction.turnLeft(); 25 | } else if (command.equals("f")) { 26 | coordinates = direction.moveForward(coordinates, MOVEMENT_DELTA); 27 | } else if (command.equals("b")) { 28 | coordinates = direction.moveBackwards(coordinates, MOVEMENT_DELTA); 29 | } 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (!(o instanceof MarsRover)) return false; 36 | 37 | MarsRover marsRover = (MarsRover) o; 38 | 39 | if (coordinates != null ? !coordinates.equals(marsRover.coordinates) : marsRover.coordinates != null) 40 | return false; 41 | return direction == marsRover.direction; 42 | 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | int result = coordinates != null ? coordinates.hashCode() : 0; 48 | result = 31 * result + (direction != null ? direction.hashCode() : 0); 49 | return result; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "MarsRover{" + 55 | "coordinates=" + coordinates + 56 | ", direction=" + direction + 57 | '}'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /katas_java/03a-mars-rover-adding-new-feature/src/test/java/MarsRoverTest.java: -------------------------------------------------------------------------------- 1 | import mars_rover.MarsRover; 2 | import org.junit.Test; 3 | 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.core.Is.is; 6 | 7 | public class MarsRoverTest { 8 | 9 | @Test 10 | public void does_nothing_when_receiving_empty_commands_sequence() { 11 | MarsRover marsRover = new MarsRover(0, 0, "N"); 12 | 13 | marsRover.receive(""); 14 | 15 | assertThat(marsRover, is(new MarsRover(0, 0, "N"))); 16 | } 17 | 18 | @Test 19 | public void turns_right_when_pointing_north() { 20 | MarsRover marsRover = new MarsRover(0, 0, "N"); 21 | 22 | marsRover.receive("r"); 23 | 24 | assertThat(marsRover, is(new MarsRover(0, 0, "E"))); 25 | } 26 | 27 | @Test 28 | public void turns_right_when_pointing_east() { 29 | MarsRover marsRover = new MarsRover(0, 0, "E"); 30 | 31 | marsRover.receive("r"); 32 | 33 | assertThat(marsRover, is(new MarsRover(0, 0, "S"))); 34 | } 35 | 36 | @Test 37 | public void turns_right_when_pointing_south() { 38 | MarsRover marsRover = new MarsRover(0, 0, "S"); 39 | 40 | marsRover.receive("r"); 41 | 42 | assertThat(marsRover, is(new MarsRover(0, 0, "W"))); 43 | } 44 | 45 | @Test 46 | public void turns_right_when_pointing_west() { 47 | MarsRover marsRover = new MarsRover(0, 0, "W"); 48 | 49 | marsRover.receive("r"); 50 | 51 | assertThat(marsRover, is(new MarsRover(0, 0, "N"))); 52 | } 53 | 54 | @Test 55 | public void turns_left_when_pointing_north() { 56 | MarsRover marsRover = new MarsRover(0, 0, "N"); 57 | 58 | marsRover.receive("l"); 59 | 60 | assertThat(marsRover, is(new MarsRover(0, 0, "W"))); 61 | } 62 | 63 | @Test 64 | public void turns_left_when_pointing_west() { 65 | MarsRover marsRover = new MarsRover(0, 0, "W"); 66 | 67 | marsRover.receive("l"); 68 | 69 | assertThat(marsRover, is(new MarsRover(0, 0, "S"))); 70 | } 71 | 72 | @Test 73 | public void turns_left_when_pointing_south() { 74 | MarsRover marsRover = new MarsRover(0, 0, "S"); 75 | 76 | marsRover.receive("l"); 77 | 78 | assertThat(marsRover, is(new MarsRover(0, 0, "E"))); 79 | } 80 | 81 | @Test 82 | public void turns_left_when_pointing_east() { 83 | MarsRover marsRover = new MarsRover(0, 0, "E"); 84 | 85 | marsRover.receive("l"); 86 | 87 | assertThat(marsRover, is(new MarsRover(0, 0, "N"))); 88 | } 89 | 90 | @Test 91 | public void moves_forward_when_pointing_north() { 92 | MarsRover marsRover = new MarsRover(5, 4, "N"); 93 | 94 | marsRover.receive("f"); 95 | 96 | assertThat(marsRover, is(new MarsRover(5, 5, "N"))); 97 | } 98 | 99 | @Test 100 | public void moves_forward_when_pointing_east() { 101 | MarsRover marsRover = new MarsRover(5, 4, "E"); 102 | 103 | marsRover.receive("f"); 104 | 105 | assertThat(marsRover, is(new MarsRover(6, 4, "E"))); 106 | } 107 | 108 | @Test 109 | public void moves_forward_when_pointing_south() { 110 | MarsRover marsRover = new MarsRover(5, 4, "S"); 111 | 112 | marsRover.receive("f"); 113 | 114 | assertThat(marsRover, is(new MarsRover(5, 3, "S"))); 115 | } 116 | 117 | @Test 118 | public void moves_forward_when_pointing_west() { 119 | MarsRover marsRover = new MarsRover(5, 4, "W"); 120 | 121 | marsRover.receive("f"); 122 | 123 | assertThat(marsRover, is(new MarsRover(4, 4, "W"))); 124 | } 125 | 126 | @Test 127 | public void moves_backward_when_pointing_north() { 128 | MarsRover marsRover = new MarsRover(5, 4, "N"); 129 | 130 | marsRover.receive("b"); 131 | 132 | assertThat(marsRover, is(new MarsRover(5, 3, "N"))); 133 | } 134 | 135 | @Test 136 | public void moves_backward_when_pointing_east() { 137 | MarsRover marsRover = new MarsRover(5, 4, "E"); 138 | 139 | marsRover.receive("b"); 140 | 141 | assertThat(marsRover, is(new MarsRover(4, 4, "E"))); 142 | } 143 | 144 | @Test 145 | public void moves_backward_when_pointing_south() { 146 | MarsRover marsRover = new MarsRover(5, 4, "S"); 147 | 148 | marsRover.receive("b"); 149 | 150 | assertThat(marsRover, is(new MarsRover(5, 5, "S"))); 151 | } 152 | 153 | @Test 154 | public void moves_backward_when_pointing_west() { 155 | MarsRover marsRover = new MarsRover(5, 4, "W"); 156 | 157 | marsRover.receive("b"); 158 | 159 | assertThat(marsRover, is(new MarsRover(6, 4, "W"))); 160 | } 161 | 162 | @Test 163 | public void receives_multiple_commands() { 164 | MarsRover marsRover = new MarsRover(7, 4, "E"); 165 | 166 | marsRover.receive("fr"); 167 | 168 | assertThat(marsRover, is(new MarsRover(8, 4, "S"))); 169 | } 170 | 171 | @Test 172 | public void ignores_unknown_commands() { 173 | MarsRover marsRover = new MarsRover(7, 4, "E"); 174 | 175 | marsRover.receive("*"); 176 | 177 | assertThat(marsRover, is(new MarsRover(7, 4, "E"))); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/README.md: -------------------------------------------------------------------------------- 1 | # Mars Rover: adding a feature to existing code (I) 2 | 3 | After: 4 | 5 | 1. Using command pattern to separate message interpretation (command creation) from command execution. 6 | 2. Extracting message interpretation responsibility to a new class. 7 | 8 | Now we're closer to being able to add the new feature easily. 9 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | mars-rover-adding-new-feature-b 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/Command.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | import mars_rover.location.Vector; 4 | 5 | public interface Command { 6 | Vector execute(Vector vector); 7 | } 8 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/MarsRover.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | import mars_rover.location.Vector; 4 | 5 | import java.util.List; 6 | 7 | public class MarsRover { 8 | 9 | private static final int DISPLACEMENT = 1; 10 | private final NasaCommunicationProtocol communicationProtocol; 11 | private Vector vector; 12 | 13 | public MarsRover(int x, int y, String direction) { 14 | this.vector = Vector.create(x, y, direction); 15 | communicationProtocol = new NasaCommunicationProtocol(); 16 | } 17 | 18 | public void receive(String message) { 19 | execute(createCommands(message)); 20 | } 21 | 22 | private void execute(List commands) { 23 | for (Command command : commands) { 24 | vector = command.execute(vector); 25 | } 26 | } 27 | 28 | private List createCommands(String message) { 29 | return communicationProtocol.createCommands(message, DISPLACEMENT); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | 37 | MarsRover marsRover = (MarsRover) o; 38 | 39 | return vector != null ? vector.equals(marsRover.vector) : marsRover.vector == null; 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return vector != null ? vector.hashCode() : 0; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "MarsRover{" + 50 | "vector=" + vector + 51 | '}'; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/NasaCommunicationProtocol.java: -------------------------------------------------------------------------------- 1 | package mars_rover; 2 | 3 | import mars_rover.command_types.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | class NasaCommunicationProtocol { 9 | List createCommands(String message, int displacement) { 10 | List commands = new ArrayList<>(); 11 | for (String commandRepresentation : parseMessage(message)) { 12 | commands.add(createCommand(commandRepresentation, displacement)); 13 | } 14 | return commands; 15 | } 16 | 17 | private String[] parseMessage(String message) { 18 | return message.split(""); 19 | } 20 | 21 | private Command createCommand(String commandRepresentation, int displacement) { 22 | 23 | if (commandRepresentation.equals("r")) { 24 | return new TurningRight(); 25 | } else if (commandRepresentation.equals("l")) { 26 | return new TurningLeft(); 27 | } else if (commandRepresentation.equals("f")) { 28 | return new MovingForward(displacement); 29 | } else if (commandRepresentation.equals("b")) { 30 | return new MovingBackwards(displacement); 31 | } else { 32 | return new UnknownCommand(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/MovingBackwards.java: -------------------------------------------------------------------------------- 1 | package mars_rover.command_types; 2 | 3 | import mars_rover.Command; 4 | import mars_rover.location.Vector; 5 | 6 | public class MovingBackwards implements Command { 7 | private int delta; 8 | 9 | public MovingBackwards(int delta) { 10 | this.delta = delta; 11 | } 12 | 13 | @Override 14 | public Vector execute(Vector vector) { 15 | return vector.moveBackwards(delta); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/MovingForward.java: -------------------------------------------------------------------------------- 1 | package mars_rover.command_types; 2 | 3 | import mars_rover.Command; 4 | import mars_rover.location.Vector; 5 | 6 | public class MovingForward implements Command { 7 | private int delta; 8 | 9 | public MovingForward(int delta) { 10 | this.delta = delta; 11 | } 12 | 13 | @Override 14 | public Vector execute(Vector vector) { 15 | return vector.moveForward(delta); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/TurningLeft.java: -------------------------------------------------------------------------------- 1 | package mars_rover.command_types; 2 | 3 | import mars_rover.Command; 4 | import mars_rover.location.Vector; 5 | 6 | public class TurningLeft implements Command { 7 | @Override 8 | public Vector execute(Vector vector) { 9 | return vector.turnLeft(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/TurningRight.java: -------------------------------------------------------------------------------- 1 | package mars_rover.command_types; 2 | 3 | import mars_rover.Command; 4 | import mars_rover.location.Vector; 5 | 6 | public class TurningRight implements Command { 7 | @Override 8 | public Vector execute(Vector vector) { 9 | return vector.turnRight(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/UnknownCommand.java: -------------------------------------------------------------------------------- 1 | package mars_rover.command_types; 2 | 3 | import mars_rover.Command; 4 | import mars_rover.location.Vector; 5 | 6 | public class UnknownCommand implements Command { 7 | @Override 8 | public Vector execute(Vector vector) { 9 | return vector; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Coordinates.java: -------------------------------------------------------------------------------- 1 | package mars_rover.location; 2 | 3 | class Coordinates { 4 | 5 | private final int x; 6 | private final int y; 7 | 8 | Coordinates(int x, int y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | Coordinates incrementX(int delta) { 14 | return new Coordinates(x + delta, y); 15 | } 16 | 17 | Coordinates incrementY(int delta) { 18 | return new Coordinates(x, y + delta); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "Coordinates{" + 24 | "x=" + x + 25 | ", y=" + y + 26 | '}'; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) return true; 32 | if (o == null || getClass() != o.getClass()) return false; 33 | 34 | Coordinates that = (Coordinates) o; 35 | 36 | if (x != that.x) return false; 37 | return y == that.y; 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int result = x; 43 | result = 31 * result + y; 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Direction.java: -------------------------------------------------------------------------------- 1 | package mars_rover.location; 2 | 3 | enum Direction { 4 | North { 5 | public Direction turnRight() { 6 | return East; 7 | } 8 | 9 | public Direction turnLeft() { 10 | return West; 11 | } 12 | 13 | public Coordinates moveForward(Coordinates coordinates, int delta) { 14 | return coordinates.incrementY(delta); 15 | } 16 | 17 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 18 | return coordinates.incrementY(-delta); 19 | } 20 | }, 21 | South { 22 | public Direction turnRight() { 23 | return West; 24 | } 25 | 26 | public Direction turnLeft() { 27 | return East; 28 | } 29 | 30 | public Coordinates moveForward(Coordinates coordinates, int delta) { 31 | return coordinates.incrementY(-delta); 32 | } 33 | 34 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 35 | return coordinates.incrementY(delta); 36 | } 37 | }, 38 | East { 39 | public Direction turnRight() { 40 | return South; 41 | } 42 | 43 | public Direction turnLeft() { 44 | return North; 45 | } 46 | 47 | public Coordinates moveForward(Coordinates coordinates, int delta) { 48 | return coordinates.incrementX(delta); 49 | } 50 | 51 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 52 | return coordinates.incrementX(-delta); 53 | } 54 | }, 55 | West { 56 | public Direction turnRight() { 57 | return North; 58 | } 59 | 60 | public Direction turnLeft() { 61 | return South; 62 | } 63 | 64 | public Coordinates moveForward(Coordinates coordinates, int delta) { 65 | return coordinates.incrementX(-delta); 66 | } 67 | 68 | public Coordinates moveBackwards(Coordinates coordinates, int delta) { 69 | return coordinates.incrementX(delta); 70 | } 71 | }; 72 | 73 | static public Direction parse(String directionAsString) { 74 | if (directionAsString.equals("N")) { 75 | return North; 76 | } else if (directionAsString.equals("E")) { 77 | return East; 78 | } else if (directionAsString.equals("S")) { 79 | return South; 80 | } else { 81 | return West; 82 | } 83 | } 84 | 85 | abstract public Direction turnRight(); 86 | 87 | abstract public Direction turnLeft(); 88 | 89 | abstract public Coordinates moveForward(Coordinates coordinates, int delta); 90 | 91 | abstract public Coordinates moveBackwards(Coordinates coordinates, int delta); 92 | } 93 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Vector.java: -------------------------------------------------------------------------------- 1 | package mars_rover.location; 2 | 3 | public class Vector { 4 | 5 | private final Coordinates coordinates; 6 | private final Direction direction; 7 | 8 | public static Vector create(int x, int y, String direction) { 9 | return new Vector(new Coordinates(x, y), Direction.parse(direction)); 10 | } 11 | 12 | private Vector(Coordinates coordinates, Direction direction) { 13 | this.coordinates = coordinates; 14 | this.direction = direction; 15 | } 16 | 17 | public Vector turnRight() { 18 | return new Vector(coordinates, direction.turnRight()); 19 | } 20 | 21 | public Vector turnLeft() { 22 | return new Vector(coordinates, direction.turnLeft()); 23 | } 24 | 25 | public Vector moveForward(int delta) { 26 | return new Vector(direction.moveForward(coordinates, delta), direction); 27 | } 28 | 29 | public Vector moveBackwards(int delta) { 30 | return new Vector(direction.moveBackwards(coordinates, delta), direction); 31 | } 32 | 33 | @Override 34 | public boolean equals(Object o) { 35 | if (this == o) return true; 36 | if (o == null || getClass() != o.getClass()) return false; 37 | 38 | Vector vector = (Vector) o; 39 | 40 | if (coordinates != null ? !coordinates.equals(vector.coordinates) : vector.coordinates != null) return false; 41 | return direction == vector.direction; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | int result = coordinates != null ? coordinates.hashCode() : 0; 47 | result = 31 * result + (direction != null ? direction.hashCode() : 0); 48 | return result; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "Vector{" + 54 | "coordinates=" + coordinates + 55 | ", direction=" + direction + 56 | '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /katas_java/03b-mars-rover-adding-new-feature/src/test/java/MarsRoverTest.java: -------------------------------------------------------------------------------- 1 | import mars_rover.MarsRover; 2 | import org.junit.Test; 3 | 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.core.Is.is; 6 | 7 | public class MarsRoverTest { 8 | 9 | @Test 10 | public void does_nothing_when_receiving_empty_commands_sequence() { 11 | MarsRover marsRover = new MarsRover(0,0, "N"); 12 | 13 | marsRover.receive(""); 14 | 15 | assertThat(marsRover, is(new MarsRover(0,0, "N"))); 16 | } 17 | 18 | @Test 19 | public void turns_right_when_pointing_north() { 20 | MarsRover marsRover = new MarsRover(0,0, "N"); 21 | 22 | marsRover.receive("r"); 23 | 24 | assertThat(marsRover, is(new MarsRover(0,0, "E"))); 25 | } 26 | 27 | @Test 28 | public void turns_right_when_pointing_east() { 29 | MarsRover marsRover = new MarsRover(0,0, "E"); 30 | 31 | marsRover.receive("r"); 32 | 33 | assertThat(marsRover, is(new MarsRover(0,0, "S"))); 34 | } 35 | 36 | @Test 37 | public void turns_right_when_pointing_south() { 38 | MarsRover marsRover = new MarsRover(0,0, "S"); 39 | 40 | marsRover.receive("r"); 41 | 42 | assertThat(marsRover, is(new MarsRover(0,0, "W"))); 43 | } 44 | 45 | @Test 46 | public void turns_right_when_pointing_west() { 47 | MarsRover marsRover = new MarsRover(0,0, "W"); 48 | 49 | marsRover.receive("r"); 50 | 51 | assertThat(marsRover, is(new MarsRover(0,0, "N"))); 52 | } 53 | 54 | @Test 55 | public void turns_left_when_pointing_north() { 56 | MarsRover marsRover = new MarsRover(0,0, "N"); 57 | 58 | marsRover.receive("l"); 59 | 60 | assertThat(marsRover, is(new MarsRover(0,0, "W"))); 61 | } 62 | 63 | @Test 64 | public void turns_left_when_pointing_west() { 65 | MarsRover marsRover = new MarsRover(0,0, "W"); 66 | 67 | marsRover.receive("l"); 68 | 69 | assertThat(marsRover, is(new MarsRover(0,0, "S"))); 70 | } 71 | 72 | @Test 73 | public void turns_left_when_pointing_south() { 74 | MarsRover marsRover = new MarsRover(0,0, "S"); 75 | 76 | marsRover.receive("l"); 77 | 78 | assertThat(marsRover, is(new MarsRover(0,0, "E"))); 79 | } 80 | 81 | @Test 82 | public void turns_left_when_pointing_east() { 83 | MarsRover marsRover = new MarsRover(0,0, "E"); 84 | 85 | marsRover.receive("l"); 86 | 87 | assertThat(marsRover, is(new MarsRover(0,0, "N"))); 88 | } 89 | 90 | @Test 91 | public void moves_forward_when_pointing_north() { 92 | MarsRover marsRover = new MarsRover(5,4, "N"); 93 | 94 | marsRover.receive("f"); 95 | 96 | assertThat(marsRover, is(new MarsRover(5,5, "N"))); 97 | } 98 | 99 | @Test 100 | public void moves_forward_when_pointing_east() { 101 | MarsRover marsRover = new MarsRover(5,4, "E"); 102 | 103 | marsRover.receive("f"); 104 | 105 | assertThat(marsRover, is(new MarsRover(6,4, "E"))); 106 | } 107 | 108 | @Test 109 | public void moves_forward_when_pointing_south() { 110 | MarsRover marsRover = new MarsRover(5,4, "S"); 111 | 112 | marsRover.receive("f"); 113 | 114 | assertThat(marsRover, is(new MarsRover(5,3, "S"))); 115 | } 116 | 117 | @Test 118 | public void moves_forward_when_pointing_west() { 119 | MarsRover marsRover = new MarsRover(5,4, "W"); 120 | 121 | marsRover.receive("f"); 122 | 123 | assertThat(marsRover, is(new MarsRover(4,4, "W"))); 124 | } 125 | 126 | @Test 127 | public void moves_backward_when_pointing_north() { 128 | MarsRover marsRover = new MarsRover(5,4, "N"); 129 | 130 | marsRover.receive("b"); 131 | 132 | assertThat(marsRover, is(new MarsRover(5,3, "N"))); 133 | } 134 | 135 | @Test 136 | public void moves_backward_when_pointing_east() { 137 | MarsRover marsRover = new MarsRover(5,4, "E"); 138 | 139 | marsRover.receive("b"); 140 | 141 | assertThat(marsRover, is(new MarsRover(4,4, "E"))); 142 | } 143 | 144 | @Test 145 | public void moves_backward_when_pointing_south() { 146 | MarsRover marsRover = new MarsRover(5,4, "S"); 147 | 148 | marsRover.receive("b"); 149 | 150 | assertThat(marsRover, is(new MarsRover(5,5, "S"))); 151 | } 152 | 153 | @Test 154 | public void moves_backward_when_pointing_west() { 155 | MarsRover marsRover = new MarsRover(5,4, "W"); 156 | 157 | marsRover.receive("b"); 158 | 159 | assertThat(marsRover, is(new MarsRover(6,4, "W"))); 160 | } 161 | 162 | @Test 163 | public void receives_multiple_commands() { 164 | MarsRover marsRover = new MarsRover(7, 4, "E"); 165 | 166 | marsRover.receive("fr"); 167 | 168 | assertThat(marsRover, is(new MarsRover(8, 4, "S"))); 169 | } 170 | 171 | @Test 172 | public void ignores_unknown_commands() { 173 | MarsRover marsRover = new MarsRover(7, 4, "E"); 174 | 175 | marsRover.receive("*"); 176 | 177 | assertThat(marsRover, is(new MarsRover(7, 4, "E"))); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /katas_java/04-working-with-code-we-do-not-own/README.md: -------------------------------------------------------------------------------- 1 | ### Brownish Greenfield Gilded Rose Kata* 2 | 3 | Hi and welcome to team Gilded Rose! 4 | 5 | As you know, we are a small inn with a prime location in a prominent city ran by a friendly innkeeper named Allison. We also buy and sell only the finest goods. 6 | 7 | Unfortunately, our goods are constantly degrading in quality as they approach their sell by date. We have a system in place that updates our inventory for us. It was developed by a no-nonsense type named Leeroy, who has moved on to new adventures. 8 | 9 | Your task is to rewrite the system using an old class, that already exists. First an introduction to our system: 10 | 11 | * All items have a SellIn value which denotes the number of days we have to sell the item 12 | * All items have a Quality value which denotes how valuable the item is 13 | * At the end of each day our system lowers both values for every item 14 | 15 | Pretty simple, right? Well this is where it gets interesting: 16 | 17 | * Once the sell by date has passed, Quality degrades twice as fast 18 | * The Quality of an item is never negative 19 | * “Aged Brie” actually increases in Quality the older it gets 20 | * “Sulfuras”, being a legendary item, never has to be sold or decreases in Quality 21 | * The Quality of an item is never more than 50, however “Sulfuras” is a legendary item and as such its Quality is 80 and it never alters. 22 | * “Backstage passes”, like aged brie, increases in Quality as it’s SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concert 23 | * “Conjured” items degrade in Quality twice as fast as normal items 24 | 25 | Your task is to create the system from scratch, with only a restriction: 26 | **You must use the Item class that Leeroy developed**. 27 | 28 | But beware, before leaving Leeroy casted a curse on this class, so: 29 | 30 | 1. It must be used. 31 | 2. It can not be modified. This prohibition applies to modifying it, deleting it, adding new methods, deleting methodts, changing visibility of stuff, etc. 32 | 3. It cannot be extended. 33 | 34 | If you break any of these rules a goblin in the corner will insta-rage and destroy you. 35 | 36 | You'll find the initial code for this kata in this repository. 37 | 38 | Good luck and beware the goblin! 39 | 40 | *Adapted from the [Brownish Greenfield Gilded Rose Kata](https://alvarogarcia7.github.io/blog/2016/04/18/brownish-greenfield-gilded-rose-kata-formulation/) -------------------------------------------------------------------------------- /katas_java/04-working-with-code-we-do-not-own/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | working-with-code-we-do-not-own 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /katas_java/04-working-with-code-we-do-not-own/src/main/java/items/Item.java: -------------------------------------------------------------------------------- 1 | package items; 2 | 3 | public class Item { 4 | 5 | public String name; 6 | 7 | public int sellIn; 8 | 9 | public int quality; 10 | 11 | public Item(String name, int sellIn, int quality) { 12 | this.name = name; 13 | this.sellIn = sellIn; 14 | this.quality = quality; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return this.name + ", " + this.sellIn + ", " + this.quality; 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /katas_java/04-working-with-code-we-do-not-own/src/test/java/GildedRoseTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.is; 5 | 6 | public class GildedRoseTest { 7 | @Test 8 | public void shouldFail() { 9 | assertThat(false, is(true)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/README.md: -------------------------------------------------------------------------------- 1 | ## Goal 2 | This code computes the prices of the beverages served in our coffe house. 3 | 4 | We'd like you to add an optional cinnamon supplement that costs 0.05€ 5 | to all our existing catalog of beverages. 6 | 7 | ### Tips: 8 | You might want to refactor the code a bit first. 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | katas-java 4 | beverages_prices 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.13.1 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Beverage.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public interface Beverage { 4 | double price(); 5 | } 6 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Coffee.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class Coffee implements Beverage { 4 | @Override 5 | public double price() { 6 | return 1.2; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/CoffeeWithMilk.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class CoffeeWithMilk extends Coffee { 4 | @Override 5 | public double price() { 6 | return super.price() + 0.10; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/CoffeeWithMilkAndCream.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class CoffeeWithMilkAndCream extends Coffee { 4 | @Override 5 | public double price() { 6 | return super.price() + 0.25; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/HotChocolate.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class HotChocolate implements Beverage { 4 | @Override 5 | public double price() { 6 | return 1.45; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/HotChocolateWithCream.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class HotChocolateWithCream extends HotChocolate { 4 | @Override 5 | public double price() { 6 | return 1.45 + 0.15; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Tea.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class Tea implements Beverage { 4 | @Override 5 | public double price() { 6 | return 1.5; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/TeaWithMilk.java: -------------------------------------------------------------------------------- 1 | package beverages; 2 | 3 | public class TeaWithMilk extends Tea { 4 | @Override 5 | public double price() { 6 | return super.price() + 0.10; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/test/java/unit_tests/BeveragesPricingTest.java: -------------------------------------------------------------------------------- 1 | package unit_tests; 2 | 3 | import beverages.*; 4 | import org.junit.Test; 5 | 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.Matchers.closeTo; 9 | 10 | public class BeveragesPricingTest { 11 | @Test 12 | public void computes_coffee_price() { 13 | Beverage coffee = new Coffee(); 14 | assertThat(coffee.price(), is(closeTo(1.20, 0.001))); 15 | } 16 | 17 | @Test 18 | public void computes_tea_price() { 19 | Beverage tea = new Tea(); 20 | assertThat(tea.price(), is(closeTo(1.50, 0.001))); 21 | } 22 | 23 | @Test 24 | public void computes_hot_chocolate_price() { 25 | Beverage hotChocolate = new HotChocolate(); 26 | assertThat(hotChocolate.price(), is(closeTo(1.45, 0.001))); 27 | } 28 | 29 | @Test 30 | public void computes_tea_with_milk_price() { 31 | Tea teaWithMilk = new TeaWithMilk(); 32 | assertThat(teaWithMilk.price(), is(closeTo(1.60, 0.001))); 33 | } 34 | 35 | @Test 36 | public void computes_coffee_with_milk_price() { 37 | Coffee coffeeWithMilk = new CoffeeWithMilk(); 38 | assertThat(coffeeWithMilk.price(), is(closeTo(1.30, 0.001))); 39 | } 40 | 41 | @Test 42 | public void computes_coffee_with_milk_and_cream_price() { 43 | Coffee coffeeWithMilkAndCream = new CoffeeWithMilkAndCream(); 44 | assertThat(coffeeWithMilkAndCream.price(), is(closeTo(1.45, 0.001))); 45 | } 46 | 47 | @Test 48 | public void computes_hot_chocolate_with_cream_price() { 49 | HotChocolateWithCream hotChocolateWithCream = new HotChocolateWithCream(); 50 | assertThat(hotChocolateWithCream.price(), is(closeTo(1.60, 0.001))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | refactgoring-to-hexagonal-architecture 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | jar 19 | test 20 | 21 | 22 | 23 | junit 24 | junit 25 | 4.13.1 26 | jar 27 | test 28 | 29 | 30 | 31 | org.mockito 32 | mockito-all 33 | 1.10.8 34 | test 35 | 36 | 37 | 38 | com.sun.mail 39 | javax.mail 40 | 1.5.5 41 | 42 | 43 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/readme.md: -------------------------------------------------------------------------------- 1 | # Birthday Greetings Kata 2 | 3 | We'll refactor a very convoluted service to segregate its business logic from its infrastructure logic. 4 | 5 | We'll use an architectural style called [hexagonal architecture or ports and adapters](http://alistair.cockburn.us/Hexagonal+architecture). 6 | 7 | To practice we'll do [Matteo Vaccari](http://matteo.vaccari.name/blog/)'s Birthday Greetings Refactoring Kata. 8 | 9 | To learn more about this great kata, read Matteo's post about it on its blog: [The birthday greetings kata](http://matteo.vaccari.name/blog/archives/154). That post also constains a great explanation of the hexagonal architecture. 10 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/BirthdayService.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.text.ParseException; 7 | 8 | import javax.mail.Message; 9 | import javax.mail.MessagingException; 10 | import javax.mail.Session; 11 | import javax.mail.Transport; 12 | import javax.mail.internet.AddressException; 13 | import javax.mail.internet.InternetAddress; 14 | import javax.mail.internet.MimeMessage; 15 | 16 | public class BirthdayService { 17 | 18 | public void sendGreetings(String fileName, OurDate ourDate, 19 | String smtpHost, int smtpPort) throws IOException, ParseException, 20 | AddressException, MessagingException { 21 | BufferedReader in = new BufferedReader(new FileReader(fileName)); 22 | String str = ""; 23 | str = in.readLine(); // skip header 24 | while ((str = in.readLine()) != null) { 25 | String[] employeeData = str.split(", "); 26 | Employee employee = new Employee(employeeData[1], employeeData[0], 27 | employeeData[2], employeeData[3]); 28 | if (employee.isBirthday(ourDate)) { 29 | String recipient = employee.getEmail(); 30 | String body = "Happy Birthday, dear %NAME%!".replace("%NAME%", 31 | employee.getFirstName()); 32 | String subject = "Happy Birthday!"; 33 | sendMessage(smtpHost, smtpPort, "sender@here.com", subject, 34 | body, recipient); 35 | } 36 | } 37 | } 38 | 39 | private void sendMessage(String smtpHost, int smtpPort, String sender, 40 | String subject, String body, String recipient) 41 | throws AddressException, MessagingException { 42 | // Create a mail session 43 | java.util.Properties props = new java.util.Properties(); 44 | props.put("mail.smtp.host", smtpHost); 45 | props.put("mail.smtp.port", "" + smtpPort); 46 | Session session = Session.getDefaultInstance(props, null); 47 | 48 | // Construct the message 49 | Message msg = new MimeMessage(session); 50 | msg.setFrom(new InternetAddress(sender)); 51 | msg.setRecipient(Message.RecipientType.TO, new InternetAddress( 52 | recipient)); 53 | msg.setSubject(subject); 54 | msg.setText(body); 55 | 56 | // Send the message 57 | sendMessage(msg); 58 | } 59 | 60 | // made protected for testing :-( 61 | protected void sendMessage(Message msg) throws MessagingException { 62 | Transport.send(msg); 63 | } 64 | 65 | public static void main(String[] args) { 66 | BirthdayService service = new BirthdayService(); 67 | try { 68 | service.sendGreetings("employee_data.txt", 69 | new OurDate("2008/10/08"), "localhost", 25); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/Employee.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings; 2 | 3 | import java.text.ParseException; 4 | 5 | public class Employee { 6 | 7 | private OurDate birthDate; 8 | private String lastName; 9 | private String firstName; 10 | private String email; 11 | 12 | public Employee(String firstName, String lastName, String birthDate, 13 | String email) throws ParseException { 14 | this.firstName = firstName; 15 | this.lastName = lastName; 16 | this.birthDate = new OurDate(birthDate); 17 | this.email = email; 18 | } 19 | 20 | public boolean isBirthday(OurDate today) { 21 | return today.isSameDay(birthDate); 22 | } 23 | 24 | public String getEmail() { 25 | return email; 26 | } 27 | 28 | public String getFirstName() { 29 | return firstName; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Employee " + firstName + " " + lastName + " <" + email 35 | + "> born " + birthDate; 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | final int prime = 31; 41 | int result = 1; 42 | result = prime * result 43 | + ((birthDate == null) ? 0 : birthDate.hashCode()); 44 | result = prime * result + ((email == null) ? 0 : email.hashCode()); 45 | result = prime * result 46 | + ((firstName == null) ? 0 : firstName.hashCode()); 47 | result = prime * result 48 | + ((lastName == null) ? 0 : lastName.hashCode()); 49 | return result; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object obj) { 54 | if (this == obj) 55 | return true; 56 | if (obj == null) 57 | return false; 58 | if (!(obj instanceof Employee)) 59 | return false; 60 | Employee other = (Employee) obj; 61 | if (birthDate == null) { 62 | if (other.birthDate != null) 63 | return false; 64 | } else if (!birthDate.equals(other.birthDate)) 65 | return false; 66 | if (email == null) { 67 | if (other.email != null) 68 | return false; 69 | } else if (!email.equals(other.email)) 70 | return false; 71 | if (firstName == null) { 72 | if (other.firstName != null) 73 | return false; 74 | } else if (!firstName.equals(other.firstName)) 75 | return false; 76 | if (lastName == null) { 77 | if (other.lastName != null) 78 | return false; 79 | } else if (!lastName.equals(other.lastName)) 80 | return false; 81 | return true; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/OurDate.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | import java.util.GregorianCalendar; 7 | 8 | public class OurDate { 9 | 10 | private Date date; 11 | 12 | public OurDate(String yyyyMMdd) throws ParseException { 13 | date = new SimpleDateFormat("yyyy/MM/dd").parse(yyyyMMdd); 14 | } 15 | 16 | public int getDay() { 17 | return getPartOfDate(GregorianCalendar.DAY_OF_MONTH); 18 | } 19 | 20 | public int getMonth() { 21 | return 1 + getPartOfDate(GregorianCalendar.MONTH); 22 | } 23 | 24 | public boolean isSameDay(OurDate anotherDate) { 25 | return anotherDate.getDay() == this.getDay() 26 | && anotherDate.getMonth() == this.getMonth(); 27 | } 28 | 29 | @Override 30 | public int hashCode() { 31 | return date.hashCode(); 32 | } 33 | 34 | @Override 35 | public boolean equals(Object obj) { 36 | if (!(obj instanceof OurDate)) 37 | return false; 38 | OurDate other = (OurDate) obj; 39 | return other.date.equals(this.date); 40 | } 41 | 42 | private int getPartOfDate(int part) { 43 | GregorianCalendar calendar = new GregorianCalendar(); 44 | calendar.setTime(date); 45 | return calendar.get(part); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/AcceptanceTest.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.test; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.mail.Message; 7 | import javax.mail.MessagingException; 8 | 9 | import birthdaygreetings.BirthdayService; 10 | import birthdaygreetings.OurDate; 11 | 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | public class AcceptanceTest { 18 | 19 | private static final int SMTP_PORT = 25; 20 | private List messagesSent; 21 | private BirthdayService service; 22 | 23 | @Before 24 | public void setUp() throws Exception { 25 | messagesSent = new ArrayList(); 26 | 27 | service = new BirthdayService() { 28 | @Override 29 | protected void sendMessage(Message msg) throws MessagingException { 30 | messagesSent.add(msg); 31 | } 32 | }; 33 | } 34 | 35 | @Test 36 | public void baseScenario() throws Exception { 37 | 38 | service.sendGreetings("src/test/resources/employee_data.txt", 39 | new OurDate("2008/10/08"), "localhost", SMTP_PORT); 40 | 41 | assertEquals("message not sent?", 1, messagesSent.size()); 42 | Message message = messagesSent.get(0); 43 | assertEquals("Happy Birthday, dear John!", message.getContent()); 44 | assertEquals("Happy Birthday!", message.getSubject()); 45 | assertEquals(1, message.getAllRecipients().length); 46 | assertEquals("john.doe@foobar.com", 47 | message.getAllRecipients()[0].toString()); 48 | } 49 | 50 | @Test 51 | public void willNotSendEmailsWhenNobodysBirthday() throws Exception { 52 | service.sendGreetings("src/test/resources/employee_data.txt", 53 | new OurDate("2008/01/01"), "localhost", SMTP_PORT); 54 | 55 | assertEquals("what? messages?", 0, messagesSent.size()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/EmployeeTest.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.test; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import birthdaygreetings.Employee; 6 | import birthdaygreetings.OurDate; 7 | 8 | import org.junit.Test; 9 | 10 | public class EmployeeTest { 11 | 12 | @Test 13 | public void testBirthday() throws Exception { 14 | Employee employee = new Employee("foo", "bar", "1990/01/31", "a@b.c"); 15 | assertFalse("not his birthday", 16 | employee.isBirthday(new OurDate("2008/01/30"))); 17 | assertTrue("his birthday", 18 | employee.isBirthday(new OurDate("2008/01/31"))); 19 | } 20 | 21 | @Test 22 | public void equality() throws Exception { 23 | Employee base = new Employee("First", "Last", "1999/09/01", 24 | "first@last.com"); 25 | Employee same = new Employee("First", "Last", "1999/09/01", 26 | "first@last.com"); 27 | Employee different = new Employee("First", "Last", "1999/09/01", 28 | "boom@boom.com"); 29 | 30 | assertFalse(base.equals(null)); 31 | assertFalse(base.equals("")); 32 | assertTrue(base.equals(same)); 33 | assertFalse(base.equals(different)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/OurDateTest.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.test; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import birthdaygreetings.OurDate; 6 | 7 | import org.junit.Test; 8 | 9 | public class OurDateTest { 10 | @Test 11 | public void getters() throws Exception { 12 | OurDate ourDate = new OurDate("1789/01/24"); 13 | assertEquals(1, ourDate.getMonth()); 14 | assertEquals(24, ourDate.getDay()); 15 | } 16 | 17 | @Test 18 | public void isSameDate() throws Exception { 19 | OurDate ourDate = new OurDate("1789/01/24"); 20 | OurDate sameDay = new OurDate("2001/01/24"); 21 | OurDate notSameDay = new OurDate("1789/01/25"); 22 | OurDate notSameMonth = new OurDate("1789/02/25"); 23 | 24 | assertTrue("same", ourDate.isSameDay(sameDay)); 25 | assertFalse("not same day", ourDate.isSameDay(notSameDay)); 26 | assertFalse("not same month", ourDate.isSameDay(notSameMonth)); 27 | } 28 | 29 | @Test 30 | public void equality() throws Exception { 31 | OurDate base = new OurDate("2000/01/02"); 32 | OurDate same = new OurDate("2000/01/02"); 33 | OurDate different = new OurDate("2000/01/04"); 34 | 35 | assertFalse(base.equals(null)); 36 | assertFalse(base.equals("")); 37 | assertTrue(base.equals(base)); 38 | assertTrue(base.equals(same)); 39 | assertFalse(base.equals(different)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /katas_java/06-refactoring-to-hexagonal-architecture/src/test/resources/employee_data.txt: -------------------------------------------------------------------------------- 1 | last_name, first_name, date_of_birth, email 2 | Doe, John, 1982/10/08, john.doe@foobar.com 3 | Ann, Mary, 1975/03/11, mary.ann@foobar.com 4 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.codesai.katas-java 4 | refactgoring-to-hexagonal-architecture-2 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | jar 18 | test 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.13.1 25 | jar 26 | test 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | 1.10.8 33 | test 34 | 35 | 36 | 37 | com.sun.mail 38 | javax.mail 39 | 1.5.5 40 | 41 | 42 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/readme.md: -------------------------------------------------------------------------------- 1 | # Birthday Greetings Kata 2 2 | 3 | We'll continue refactoring a now less convoluted service to extract the remaining infrastructure logic. 4 | 5 | We'll use an architectural style called [hexagonal architecture or ports and adapters](http://alistair.cockburn.us/Hexagonal+architecture). 6 | 7 | To practice we'll do [Matteo Vaccari](http://matteo.vaccari.name/blog/)'s Birthday Greetings Refactoring Kata. 8 | 9 | To learn more about this great kata, read Matteo's post about it on its blog: [The birthday greetings kata](http://matteo.vaccari.name/blog/archives/154). That post also constains a great explanation of the hexagonal architecture. -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/application/BirthdayService.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.application; 2 | 3 | import birthdaygreetings.core.Employee; 4 | import birthdaygreetings.core.EmployeesRepository; 5 | import birthdaygreetings.core.GreetingMessage; 6 | import birthdaygreetings.core.OurDate; 7 | 8 | import javax.mail.Message; 9 | import javax.mail.MessagingException; 10 | import javax.mail.Session; 11 | import javax.mail.Transport; 12 | import javax.mail.internet.InternetAddress; 13 | import javax.mail.internet.MimeMessage; 14 | import java.util.List; 15 | 16 | public class BirthdayService { 17 | 18 | private EmployeesRepository employeesRepository; 19 | 20 | public BirthdayService(EmployeesRepository employeesRepository) { 21 | this.employeesRepository = employeesRepository; 22 | } 23 | 24 | public void sendGreetings(OurDate date, String smtpHost, int smtpPort, String sender) throws MessagingException { 25 | 26 | send(greetingMessagesFor(employeesHavingBirthday(date)), 27 | smtpHost, smtpPort, sender); 28 | } 29 | 30 | private List greetingMessagesFor(List employees) { 31 | return GreetingMessage.generateForSome(employees); 32 | } 33 | 34 | private List employeesHavingBirthday(OurDate today) { 35 | return employeesRepository.whoseBirthdayIs(today); 36 | } 37 | 38 | private void send(List messages, String smtpHost, int smtpPort, String sender) throws MessagingException { 39 | for (GreetingMessage message : messages) { 40 | String recipient = message.to(); 41 | String body = message.text(); 42 | String subject = message.subject(); 43 | sendMessage(smtpHost, smtpPort, sender, subject, body, recipient); 44 | } 45 | } 46 | 47 | private void sendMessage(String smtpHost, int smtpPort, String sender, 48 | String subject, String body, String recipient) 49 | throws MessagingException { 50 | // Create a mail session 51 | java.util.Properties props = new java.util.Properties(); 52 | props.put("mail.smtp.host", smtpHost); 53 | props.put("mail.smtp.port", "" + smtpPort); 54 | Session session = Session.getDefaultInstance(props, null); 55 | 56 | // Construct the message 57 | Message msg = new MimeMessage(session); 58 | msg.setFrom(new InternetAddress(sender)); 59 | msg.setRecipient(Message.RecipientType.TO, new InternetAddress( 60 | recipient)); 61 | msg.setSubject(subject); 62 | msg.setText(body); 63 | 64 | // Send the message 65 | sendMessage(msg); 66 | } 67 | 68 | // made protected for testing :-( 69 | protected void sendMessage(Message msg) throws MessagingException { 70 | Transport.send(msg); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/application/Main.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.application; 2 | 3 | import birthdaygreetings.core.OurDate; 4 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository; 5 | 6 | import java.util.Date; 7 | 8 | public class Main { 9 | 10 | private static final String EMPLOYEES_FILE_PATH = "employee_data.txt"; 11 | private static final String SENDER_EMAIL_ADDRESS = "sender@here.com"; 12 | private static final String HOST = "localhost"; 13 | private static final int SMTP_PORT = 25; 14 | 15 | public static void main(String[] args) { 16 | BirthdayService service = new BirthdayService( 17 | new FileEmployeesRepository(EMPLOYEES_FILE_PATH)); 18 | try { 19 | OurDate today = new OurDate(new Date()); 20 | service.sendGreetings(today, HOST, SMTP_PORT, SENDER_EMAIL_ADDRESS); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/CannotReadEmployeesException.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | public class CannotReadEmployeesException extends RuntimeException { 4 | public CannotReadEmployeesException(String cause, Exception exception) { 5 | super(cause, exception); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/Employee.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | public class Employee { 4 | 5 | private OurDate birthDate; 6 | private String lastName; 7 | private String firstName; 8 | private String email; 9 | 10 | public Employee(String firstName, String lastName, OurDate birthDate, 11 | String email) { 12 | this.firstName = firstName; 13 | this.lastName = lastName; 14 | this.birthDate = birthDate; 15 | this.email = email; 16 | } 17 | 18 | public boolean isBirthday(OurDate today) { 19 | return today.isSameDay(birthDate); 20 | } 21 | 22 | String email() { 23 | return email; 24 | } 25 | 26 | String firstName() { 27 | return firstName; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "Employee " + firstName + " " + lastName + " <" + email 33 | + "> born " + birthDate; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) return true; 39 | if (!(o instanceof Employee)) return false; 40 | 41 | Employee employee = (Employee) o; 42 | 43 | if (birthDate != null ? !birthDate.equals(employee.birthDate) : employee.birthDate != null) return false; 44 | if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false; 45 | if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false; 46 | return email != null ? email.equals(employee.email) : employee.email == null; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int result = birthDate != null ? birthDate.hashCode() : 0; 52 | result = 31 * result + (lastName != null ? lastName.hashCode() : 0); 53 | result = 31 * result + (firstName != null ? firstName.hashCode() : 0); 54 | result = 31 * result + (email != null ? email.hashCode() : 0); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/EmployeesRepository.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | import java.util.List; 4 | 5 | public interface EmployeesRepository { 6 | List whoseBirthdayIs(OurDate today); 7 | } 8 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/Greeting.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | class Greeting { 4 | private final String header; 5 | private final String content; 6 | 7 | private Greeting(String header, String content) { 8 | this.header = header; 9 | this.content = content; 10 | } 11 | 12 | static Greeting forBirthdayOf(Employee employee){ 13 | String content = String.format("Happy Birthday, dear %s!", employee.firstName()); 14 | String header = "Happy Birthday!"; 15 | return new Greeting(header, content); 16 | } 17 | 18 | String header() { 19 | return header; 20 | } 21 | 22 | String content() { 23 | return content; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/GreetingMessage.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.stream.Collectors.toList; 6 | 7 | public class GreetingMessage { 8 | 9 | private final String to; 10 | private final Greeting greeting; 11 | 12 | private GreetingMessage(String to, Greeting greeting) { 13 | this.to = to; 14 | this.greeting = greeting; 15 | } 16 | 17 | public static List generateForSome(List employees) { 18 | return employees.stream().map(GreetingMessage::generateFor).collect(toList()); 19 | } 20 | 21 | private static GreetingMessage generateFor(Employee employee) { 22 | Greeting greeting = Greeting.forBirthdayOf(employee); 23 | String recipient = employee.email(); 24 | return new GreetingMessage(recipient, greeting); 25 | } 26 | 27 | public String subject() { 28 | return this.greeting.header(); 29 | } 30 | 31 | public String text() { 32 | return this.greeting.content(); 33 | } 34 | 35 | public String to() { 36 | return this.to; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/OurDate.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.core; 2 | 3 | import java.util.Date; 4 | import java.util.GregorianCalendar; 5 | 6 | public class OurDate { 7 | private Date date; 8 | 9 | public OurDate(Date date) { 10 | this.date = date; 11 | } 12 | 13 | public boolean isSameDay(OurDate anotherDate) { 14 | return anotherDate.getDay() == this.getDay() 15 | && anotherDate.getMonth() == this.getMonth(); 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (this == o) return true; 21 | if (!(o instanceof OurDate)) return false; 22 | 23 | OurDate ourDate = (OurDate) o; 24 | 25 | return date != null ? date.equals(ourDate.date) : ourDate.date == null; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | return date != null ? date.hashCode() : 0; 31 | } 32 | 33 | private int getDay() { 34 | return getPartOfDate(GregorianCalendar.DAY_OF_MONTH); 35 | } 36 | 37 | private int getMonth() { 38 | return 1 + getPartOfDate(GregorianCalendar.MONTH); 39 | } 40 | 41 | private int getPartOfDate(int part) { 42 | GregorianCalendar calendar = new GregorianCalendar(); 43 | calendar.setTime(date); 44 | return calendar.get(part); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/DateRepresentation.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.infrastructure.repositories; 2 | 3 | import birthdaygreetings.core.OurDate; 4 | 5 | import java.text.ParseException; 6 | import java.text.SimpleDateFormat; 7 | 8 | public class DateRepresentation { 9 | private static final String DATE_FORMAT = "yyyy/MM/dd"; 10 | private final String dateAsString; 11 | 12 | public DateRepresentation(String dateAsString) { 13 | this.dateAsString = dateAsString; 14 | } 15 | 16 | public OurDate toDate() throws ParseException { 17 | return new OurDate( 18 | new SimpleDateFormat(DATE_FORMAT).parse(dateAsString) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/EmployeesFile.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.infrastructure.repositories; 2 | 3 | import birthdaygreetings.core.CannotReadEmployeesException; 4 | import birthdaygreetings.core.Employee; 5 | import birthdaygreetings.core.OurDate; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.text.ParseException; 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | class EmployeesFile { 17 | private final Iterator linesIterator; 18 | 19 | private EmployeesFile(Iterator linesIterator) { 20 | this.linesIterator = linesIterator; 21 | } 22 | 23 | public static EmployeesFile loadFrom(String path) { 24 | return new EmployeesFile(FileReader.readSkippingHeader(path)); 25 | } 26 | 27 | public List extractEmployees() { 28 | List employees = new ArrayList<>(); 29 | while (linesIterator.hasNext()) { 30 | String line = linesIterator.next(); 31 | EmployeeCsvRepresentation representation = new EmployeeCsvRepresentation(line); 32 | employees.add(representation.convertToEmployee()); 33 | } 34 | return employees; 35 | } 36 | 37 | private class EmployeeCsvRepresentation { 38 | private String content; 39 | private final String[] tokens; 40 | 41 | public EmployeeCsvRepresentation(String content) { 42 | this.content = content; 43 | this.tokens = content.split(", "); 44 | } 45 | 46 | public Employee convertToEmployee() { 47 | return new Employee(firstName(), lastName(), birthDate(), email()); 48 | } 49 | 50 | private String lastName() { 51 | return tokens[0]; 52 | } 53 | 54 | private String email() { 55 | return tokens[3]; 56 | } 57 | 58 | private String firstName() { 59 | return tokens[1]; 60 | } 61 | 62 | private OurDate birthDate() { 63 | try { 64 | return new DateRepresentation(dateAsString()).toDate(); 65 | } catch (ParseException exception) { 66 | throw new CannotReadEmployeesException( 67 | String.format("Badly formatted employee birth date in: '%s'", content), 68 | exception 69 | ); 70 | } 71 | } 72 | 73 | private String dateAsString() { 74 | return tokens[2]; 75 | } 76 | } 77 | 78 | private static class FileReader { 79 | public static Iterator readSkippingHeader(String pathString) { 80 | Path path = Paths.get(pathString); 81 | try { 82 | return skipHeader(readFile(path)); 83 | } catch (IOException exception) { 84 | throw new CannotReadEmployeesException( 85 | String.format("cannot loadFrom file = '%s'", path.toAbsolutePath()), 86 | exception 87 | ); 88 | } 89 | } 90 | 91 | private static Iterator readFile(Path path) throws IOException { 92 | List lines = Files.readAllLines(path); 93 | return lines.iterator(); 94 | } 95 | 96 | private static Iterator skipHeader(Iterator iterator) { 97 | iterator.next(); 98 | return iterator; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/FileEmployeesRepository.java: -------------------------------------------------------------------------------- 1 | package birthdaygreetings.infrastructure.repositories; 2 | 3 | import birthdaygreetings.core.Employee; 4 | import birthdaygreetings.core.EmployeesRepository; 5 | import birthdaygreetings.core.OurDate; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class FileEmployeesRepository implements EmployeesRepository { 11 | private final String path; 12 | 13 | public FileEmployeesRepository(String path) { 14 | this.path = path; 15 | } 16 | 17 | @Override 18 | public List whoseBirthdayIs(OurDate today) { 19 | return allEmployees().stream() 20 | .filter(employee -> employee.isBirthday(today)) 21 | .collect(Collectors.toList()); 22 | } 23 | 24 | private List allEmployees() { 25 | EmployeesFile employeesFile = EmployeesFile.loadFrom(path); 26 | return employeesFile.extractEmployees(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/application/BirthdayServiceAcceptanceTest.java: -------------------------------------------------------------------------------- 1 | package test.birthdaygreetings.application; 2 | 3 | import birthdaygreetings.application.BirthdayService; 4 | import birthdaygreetings.core.OurDate; 5 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import javax.mail.Message; 10 | import javax.mail.MessagingException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString; 16 | 17 | public class BirthdayServiceAcceptanceTest { 18 | 19 | private static final int SMTP_PORT = 25; 20 | private String SMTP_HOST = "localhost"; 21 | private static final String FROM = "sender@here.com"; 22 | private List messagesSent; 23 | private BirthdayService service; 24 | private static final String EMPLOYEES_FILE_PATH = "src/test/resources/employee_data.txt"; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | messagesSent = new ArrayList<>(); 29 | 30 | service = new BirthdayService(new FileEmployeesRepository(EMPLOYEES_FILE_PATH)) { 31 | @Override 32 | protected void sendMessage(Message msg) throws MessagingException { 33 | messagesSent.add(msg); 34 | } 35 | }; 36 | } 37 | 38 | @Test 39 | public void baseScenario() throws Exception { 40 | OurDate today = ourDateFromString("2008/10/08"); 41 | 42 | service.sendGreetings(today, SMTP_HOST, SMTP_PORT, FROM); 43 | 44 | assertEquals("message not sent?", 1, messagesSent.size()); 45 | Message message = messagesSent.get(0); 46 | assertEquals("Happy Birthday, dear John!", message.getContent()); 47 | assertEquals("Happy Birthday!", message.getSubject()); 48 | assertEquals(1, message.getAllRecipients().length); 49 | assertEquals("john.doe@foobar.com", message.getAllRecipients()[0].toString()); 50 | } 51 | 52 | @Test 53 | public void willNotSendEmailsWhenNobodysBirthday() throws Exception { 54 | OurDate today = ourDateFromString("2008/01/01"); 55 | 56 | service.sendGreetings(today, SMTP_HOST, SMTP_PORT, FROM); 57 | 58 | assertEquals(0, messagesSent.size()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/core/EmployeeTest.java: -------------------------------------------------------------------------------- 1 | package test.birthdaygreetings.core; 2 | 3 | import birthdaygreetings.core.Employee; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertFalse; 7 | import static org.junit.Assert.assertTrue; 8 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString; 9 | 10 | public class EmployeeTest { 11 | 12 | @Test 13 | public void testBirthday() throws Exception { 14 | Employee employee = new Employee("foo", "bar", ourDateFromString("1990/01/31"), "a@b.c"); 15 | 16 | assertFalse("no birthday", employee.isBirthday(ourDateFromString("2008/01/30"))); 17 | 18 | assertTrue("birthday", employee.isBirthday(ourDateFromString("2008/01/31"))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/core/OurDateTest.java: -------------------------------------------------------------------------------- 1 | package test.birthdaygreetings.core; 2 | 3 | import birthdaygreetings.core.OurDate; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertFalse; 7 | import static org.junit.Assert.assertTrue; 8 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString; 9 | 10 | public class OurDateTest { 11 | @Test 12 | public void identifies_if_two_dates_were_in_the_same_day() throws Exception { 13 | OurDate ourDate = ourDateFromString("1789/01/24"); 14 | OurDate sameDay = ourDateFromString("2001/01/24"); 15 | OurDate notSameDay = ourDateFromString("1789/01/25"); 16 | OurDate notSameMonth = ourDateFromString("1789/02/25"); 17 | 18 | assertTrue("same", ourDate.isSameDay(sameDay)); 19 | assertFalse("not same day", ourDate.isSameDay(notSameDay)); 20 | assertFalse("not same month", ourDate.isSameDay(notSameMonth)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/helpers/OurDateFactory.java: -------------------------------------------------------------------------------- 1 | package test.birthdaygreetings.helpers; 2 | 3 | import birthdaygreetings.core.OurDate; 4 | import birthdaygreetings.infrastructure.repositories.DateRepresentation; 5 | 6 | import java.text.ParseException; 7 | 8 | public class OurDateFactory { 9 | public static OurDate ourDateFromString(String dateAsString) throws ParseException { 10 | return new DateRepresentation(dateAsString).toDate(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/infrastructure/repositories/FileEmployeeRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package test.birthdaygreetings.infrastructure.repositories; 2 | 3 | import birthdaygreetings.core.CannotReadEmployeesException; 4 | import birthdaygreetings.core.EmployeesRepository; 5 | import birthdaygreetings.core.OurDate; 6 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | 12 | import static org.hamcrest.core.StringContains.containsString; 13 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString; 14 | 15 | public class FileEmployeeRepositoryTest { 16 | 17 | @Rule 18 | public ExpectedException expected = ExpectedException.none(); 19 | private OurDate ANY_DATE; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | ANY_DATE = ourDateFromString("2016/01/01"); 24 | } 25 | 26 | @Test 27 | public void fails_when_the_file_does_not_exist() throws Exception { 28 | 29 | EmployeesRepository employeesRepository = new FileEmployeesRepository("non-existing.file"); 30 | expected.expect(CannotReadEmployeesException.class); 31 | expected.expectMessage(containsString("cannot loadFrom file")); 32 | expected.expectMessage(containsString("non-existing.file")); 33 | 34 | employeesRepository.whoseBirthdayIs(ANY_DATE); 35 | } 36 | 37 | @Test 38 | public void fails_when_the_file_does_not_have_the_necessary_fields() throws Exception { 39 | 40 | EmployeesRepository employeesRepository = new FileEmployeesRepository("src/test/resources/wrong_data__wrong-date-format.csv"); 41 | expected.expect(CannotReadEmployeesException.class); 42 | expected.expectMessage(containsString("Badly formatted employee birth date in")); 43 | 44 | employeesRepository.whoseBirthdayIs(ANY_DATE); 45 | } 46 | } -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/employee_data.txt: -------------------------------------------------------------------------------- 1 | last_name, first_name, date_of_birth, email 2 | Doe, John, 1982/10/08, john.doe@foobar.com 3 | Ann, Mary, 1975/03/11, mary.ann@foobar.com 4 | -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/wrong_data__one-field-only.csv: -------------------------------------------------------------------------------- 1 | #this is missing two fields 2 | 1, -------------------------------------------------------------------------------- /katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/wrong_data__wrong-date-format.csv: -------------------------------------------------------------------------------- 1 | #the date format is wrong 2 | Doe, John, 2016-01-01, john.doe@foobar.com -------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/README.md: -------------------------------------------------------------------------------- 1 | ### TextConverter exercise 2 | 3 | The `HtmlTextConverter` class is designed to reformat a plain text file for display in a browser. 4 | 5 | We'll use TDD to add any new behavior to this code. 6 | 7 | Try to do the minimum changes possible to the code. 8 | 9 | #### First iteration 10 | Modify the code so that it escapes `'` and `"` substituting them by `"`. 11 | 12 | 1. Have a look at the code and identify the **change points** (the things that need to be modified 13 | to add the new behavior). 14 | 15 | 2. Write unit tests to protect the existing behavior to avoid introducing regressions when you change the code. 16 | 17 | a. First you'll have to find some **test points** (places around the **change points** where 18 | you need to add tests). 19 | 20 | * Which **test points** did you find? 21 | 22 | * How much code will the unit tests using those **test points** cover? 23 | 24 | * What would be your choice? Which criteria would you use to make that choice? 25 | 26 | b. You might need to break some dependencies before you can write the unit tests. 27 | 28 | * Which parts of the code make it difficult to write unit tests? 29 | 30 | * Which dependency-breaking technique will you need to be able to write unit tests? 31 | 32 | * Does the chosen dependency-breaking technique depend on the chosen **test points**? 33 | 34 | * Imagine you are really in a hurry, what technique could you apply? 35 | 36 | * Imagine you are not so much in a hurry, what technique could you apply? 37 | 38 | 4. Once the existing behavior is protected, start using TDD to add the new behavior. 39 | 40 | 41 | #### Second iteration 42 | 43 | Modify the code so that the html file has automatically 44 | the same name as the input file and its written in the same place. 45 | 46 | Follow the same procedure that in the previous step: 47 | 48 | 1. Identify the **change points**. 49 | 50 | 2. Identify the **test points**. 51 | 52 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies). 53 | 54 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first). 55 | 56 | #### Third iteration 57 | 58 | Modify the code so that we can create a `HtmlTextConverter` 59 | that gets the input lines from a file, 60 | or create one that gets them from a user writing on a console. 61 | 62 | The program will consume lines written by the user until it detects 63 | a line containing only `:q` which will mean the end of lines. 64 | 65 | Follow the same procedure that in the previous step: 66 | 67 | 1. Identify the **change points**. 68 | 69 | 2. Identify the **test points**. 70 | 71 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies). 72 | 73 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first). 74 | 75 | #### Fourth iteration 76 | 77 | Modify the code so that we can create an object that converts the input text into markdown, 78 | or into HTML and write it to a file. 79 | 80 | Follow the same procedure that in the previous step: 81 | 82 | 1. Identify the **change points**. 83 | 84 | 2. Identify the **test points**. 85 | 86 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies). 87 | 88 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first). 89 | 90 | * How close are you from separating the different responsibilities in the class? 91 | 92 | This kata is based on Luca Minudel's [Text Converter exercise](https://github.com/lucaminudel/TDDwithMockObjectsAndDesignPrinciples/tree/master/TDDMicroExercises/Java/TextConverter) 93 | from his [TDDwithMockObjectsAndDesignPrinciples 94 | ](https://github.com/lucaminudel/TDDwithMockObjectsAndDesignPrinciples) exercises. -------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codesai.katas-java 5 | TextConverter 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | 15 | org.hamcrest 16 | hamcrest-all 17 | 1.3 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.1 24 | 25 | 26 | 27 | org.mockito 28 | mockito-all 29 | 2.0.2-beta 30 | 31 | 32 | -------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/res/example.html: -------------------------------------------------------------------------------- 1 | Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
Line 16
Line 17
Line 18
Line 19
Line 20
Line 21
Line 22
Line 23
Line 24
Line 25
Line 26
Line 27
Line 28
Line 29
Line 30
Line 31
Line 32
Line 33
Line 34
Line 35
Line 36
Line 37
Line 38
Line 39
Line 40
Line 41
Line 42
Line 43
Line 44
Line 45
Line 46
Line 47
Line 48
Line 49
Line 50
Line 51
Line 52
Line 53
Line 54
Line 55
Line 56
Line 57
Line 58
Line 59
Line 60
Line 61
Line 62
Line 63
Line 64
Line 65
Line 66
Line 67
Line 68
Line 69
Line 70
Line 71
Line 72
Line 73
Line 74
Line 75
Line 76
Line 77
Line 78
Line 79
Line 80
Line 81
Line 82
Line 83
Line 84
Line 85
Line 86
Line 87
Line 88
Line 89
Line 90
Line 91
Line 92
Line 93
Line 94
Line 95
Line 96
Line 97
Line 98
Line 99
Line 100
-------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/res/example.txt: -------------------------------------------------------------------------------- 1 | Line 1 2 | Line 2 3 | Line 3 4 | Line 4 5 | Line 5 6 | Line 6 7 | Line 7 8 | Line 8 9 | Line 9 10 | Line 10 11 | Line 11 12 | Line 12 13 | Line 13 14 | Line 14 15 | Line 15 16 | Line 16 17 | Line 17 18 | Line 18 19 | Line 19 20 | Line 20 21 | Line 21 22 | Line 22 23 | Line 23 24 | Line 24 25 | Line 25 26 | Line 26 27 | Line 27 28 | Line 28 29 | Line 29 30 | Line 30 31 | Line 31 32 | Line 32 33 | Line 33 34 | Line 34 35 | Line 35 36 | Line 36 37 | Line 37 38 | Line 38 39 | Line 39 40 | Line 40 41 | Line 41 42 | Line 42 43 | Line 43 44 | Line 44 45 | Line 45 46 | Line 46 47 | Line 47 48 | Line 48 49 | Line 49 50 | Line 50 51 | Line 51 52 | Line 52 53 | Line 53 54 | Line 54 55 | Line 55 56 | Line 56 57 | Line 57 58 | Line 58 59 | Line 59 60 | Line 60 61 | Line 61 62 | Line 62 63 | Line 63 64 | Line 64 65 | Line 65 66 | Line 66 67 | Line 67 68 | Line 68 69 | Line 69 70 | Line 70 71 | Line 71 72 | Line 72 73 | Line 73 74 | Line 74 75 | Line 75 76 | Line 76 77 | Line 77 78 | Line 78 79 | Line 79 80 | Line 80 81 | Line 81 82 | Line 82 83 | Line 83 84 | Line 84 85 | Line 85 86 | Line 86 87 | Line 87 88 | Line 88 89 | Line 89 90 | Line 90 91 | Line 91 92 | Line 92 93 | Line 93 94 | Line 94 95 | Line 95 96 | Line 96 97 | Line 97 98 | Line 98 99 | Line 99 100 | Line 100 -------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/src/main/java/text_converter/HtmlTextConverter.java: -------------------------------------------------------------------------------- 1 | package text_converter; 2 | 3 | import java.io.*; 4 | 5 | public class HtmlTextConverter 6 | { 7 | private String fileName; 8 | 9 | public HtmlTextConverter(String fullFilenameWithPath) 10 | { 11 | this.fileName = fullFilenameWithPath; 12 | } 13 | 14 | public void convertToHtml(String htmlFile) throws IOException{ 15 | BufferedWriter writer = new BufferedWriter(new FileWriter(htmlFile)); 16 | BufferedReader reader = new BufferedReader(new FileReader(fileName)); 17 | 18 | String line = reader.readLine(); 19 | writer.write(""); 20 | while (line != null) 21 | { 22 | String output = line; 23 | output = output.replace("&", "&"); 24 | output = output.replace("<", "<"); 25 | output = output.replace(">", ">"); 26 | writer.write(output); 27 | writer.write("
"); 28 | line = reader.readLine(); 29 | } 30 | writer.write(""); 31 | writer.close(); 32 | } 33 | 34 | public String getFilename() { 35 | return this.fileName; 36 | } 37 | 38 | public static void main(String[] args) { 39 | try { 40 | HtmlTextConverter htmlTextConverter = new HtmlTextConverter("res/example.txt"); 41 | htmlTextConverter.convertToHtml("res/example.html"); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /katas_java/08-doing-tdd-on-legacy-code/src/test/java/text_converter/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/08-doing-tdd-on-legacy-code/src/test/java/text_converter/.keepme -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_1/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.codesai.katas-java 4 | outside-in-ohce-1 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | jar 18 | test 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.13.1 25 | jar 26 | test 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | 1.10.8 33 | test 34 | 35 | 36 | -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_1/readme.md: -------------------------------------------------------------------------------- 1 | # Outside-in TDD 2 | 3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html). -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_1/src/main/java/ohce/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_1/src/main/java/ohce/.keep -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_1/src/test/java/test/ohce/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_1/src/test/java/test/ohce/.keep -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.codesai.katas-java 4 | outside-in-ohce-2 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | jar 18 | test 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.13.1 25 | jar 26 | test 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | 1.10.8 33 | test 34 | 35 | 36 | -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_2/readme.md: -------------------------------------------------------------------------------- 1 | # Outside-in TDD 2 | 3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html). -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_2/src/main/java/ohce/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_2/src/main/java/ohce/.keep -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/part_2/src/test/java/test/ohce/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_2/src/test/java/test/ohce/.keep -------------------------------------------------------------------------------- /katas_java/09-outside-in-with-ohce-kata/readme.md: -------------------------------------------------------------------------------- 1 | # Outside-in TDD 2 | 3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html). -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | bin/ 5 | target 6 | 7 | .idea 8 | *.iml -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/README.md: -------------------------------------------------------------------------------- 1 | A subset of the [Bank Account kata](https://github.com/sandromancuso/Bank-kata). 2 | 3 | You have to write and make the following acceptance test pass using outside-in TDD: 4 | 5 | Given a client makes a deposit of 1000 on 10-01-2012 6 | And a deposit of 2000 on 13-01-2012 7 | And a withdrawal of 500 on 14-01-2012 8 | 9 | When she prints her bank statement 10 | 11 | Then she would see 12 | 13 | date || credit || debit || balance 14 | 14/01/2012 || || 500.00 || 2500.00 15 | 13/01/2012 || 2000.00 || || 3000.00 16 | 10/01/2012 || 1000.00 || || 1000.00 17 | 18 | The goal of the exercise is to understand the outside-in TDD style. 19 | -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.codesai.katas-java 4 | bank 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | jar 18 | test 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.13.1 25 | jar 26 | test 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | 1.10.8 33 | test 34 | 35 | 36 | -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/src/main/java/bank/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/10-outside-in-with-bank-kata/src/main/java/bank/.keep -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/acceptance/PrintingAccountStatementOnConsoleTest.java: -------------------------------------------------------------------------------- 1 | package bank.tests.acceptance; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.is; 7 | 8 | public class PrintingAccountStatementOnConsoleTest { 9 | @Test 10 | public void printing_statement_including_deposit_and_withdrawal() { 11 | // fix this test 12 | assertThat(true, is(false)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/unit/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/unit/.keep -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | bin/ 5 | target 6 | 7 | .idea 8 | *.iml -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/README.md: -------------------------------------------------------------------------------- 1 | # Unusual Spending Kata 2 | 3 | You work at a credit card company and as a value-add they want to start providing alerts to users when their spending in any particular category is higher than usual. 4 | 5 | Each `Payment` comes with a `price`, `description`, and `category`. A `Category` represents a collection of payments (e. g. "entertainment", "restaurants", "rent", ...). 6 | 7 | This is what the new service should do: 8 | 9 | For a given user, you have to compare the total amount paid for the last two months, grouped by category; and filter down to the categories for which the user spent at least 50% more this month than last month, (you'll fetch from a DB all the user payments for the current month and the previous month). 10 | 11 | Then you have to compose an e-mail message to the user that lists the categories for which spending was unusually high, with a subject like: "Unusual spending of $1076 detected!" and a body like: 12 | 13 | ``` 14 | Hello card user! 15 | 16 | We have detected unusually high spending on your card in these categories: 17 | 18 | * You spent $148 on groceries 19 | * You spent $928 on travel 20 | 21 | Love, 22 | 23 | The Credit Card Company 24 | ``` 25 | 26 | [Original kata](https://github.com/testdouble/contributing-tests/wiki/Unusual-Spending-Kata) 27 | -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.codesai.katas-java 4 | unusual-spending-alert 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | 14 | org.hamcrest 15 | hamcrest-all 16 | 1.3 17 | jar 18 | test 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.13.1 25 | jar 26 | test 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | 1.10.8 33 | test 34 | 35 | 36 | -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/src/main/java/alert_service/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/11-outside-in-with-unusual-spending-alert-service/src/main/java/alert_service/.keep -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/acceptance/AlertingUsersWithUnusualSpendingsTest.java: -------------------------------------------------------------------------------- 1 | package alert_service.tests.acceptance; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.is; 7 | 8 | public class AlertingUsersWithUnusualSpendingsTest { 9 | @Test 10 | public void alerting_users_with_unusual_spendings_in_some_categories() { 11 | // fix this test 12 | assertThat(true, is(false)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/unit/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/unit/.keep --------------------------------------------------------------------------------