-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add 'Retirement Calculator' solution
- Loading branch information
1 parent
39b8836
commit 0304a97
Showing
7 changed files
with
199 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
plugins { | ||
id 'com.craftsmanshipinsoftware.common-conventions' | ||
} | ||
|
||
dependencies { | ||
testImplementation(libs.assertj.core) | ||
testImplementation(libs.junit.jupiter) | ||
} |
11 changes: 11 additions & 0 deletions
11
retirement-calculator/src/main/java/com/craftsmanshipinsoftware/retirement/Main.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.craftsmanshipinsoftware.retirement; | ||
|
||
import java.time.Year; | ||
|
||
public class Main { | ||
|
||
public static void main(String[] args) { | ||
RetirementCalculator calculator = new RetirementCalculator(System.in, System.out, Year::now); | ||
calculator.printYearsLeft(); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...calculator/src/main/java/com/craftsmanshipinsoftware/retirement/RetirementCalculator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.craftsmanshipinsoftware.retirement; | ||
|
||
import java.io.InputStream; | ||
import java.io.PrintStream; | ||
import java.time.Year; | ||
import java.util.Objects; | ||
import java.util.Scanner; | ||
|
||
class RetirementCalculator { | ||
|
||
private final InputStream inputStream; | ||
private final PrintStream printStream; | ||
private final YearProvider yearProvider; | ||
|
||
RetirementCalculator(InputStream inputStream, PrintStream printStream, YearProvider yearProvider) { | ||
Objects.requireNonNull(inputStream, "inputStream must not be null"); | ||
Objects.requireNonNull(printStream, "printStream must not be null"); | ||
Objects.requireNonNull(yearProvider, "yearProvider must not be null"); | ||
this.inputStream = inputStream; | ||
this.printStream = printStream; | ||
this.yearProvider = yearProvider; | ||
} | ||
|
||
void printYearsLeft() { | ||
try (Scanner scanner = new Scanner(inputStream)) { | ||
int currentAge = toInt(promptForInput("What is your current age? ", scanner)); | ||
int retirementAge = toInt(promptForInput("At what age would you like to retire? ", scanner)); | ||
int yearsLeft = retirementAge - currentAge; | ||
if (yearsLeft <= 0) { | ||
this.printStream.println("You can already retire."); | ||
} else { | ||
this.printStream.printf("You have %d years left until you can retire.%n", yearsLeft); | ||
} | ||
Year currentYear = this.yearProvider.currentYear(); | ||
this.printStream.printf("It's %s, so you can retire in %s.%n", currentYear, | ||
currentYear.plusYears(yearsLeft)); | ||
} | ||
} | ||
|
||
private String promptForInput(String prompt, Scanner scanner) { | ||
this.printStream.print(prompt); | ||
String input = scanner.hasNext() ? scanner.nextLine() : null; | ||
if (input == null || input.isBlank()) { | ||
throw new IllegalArgumentException("Input must not be empty!"); | ||
} | ||
return input; | ||
} | ||
|
||
private int toInt(String value) { | ||
try { | ||
int number = Integer.parseInt(value); | ||
if (number < 0) { | ||
throw new IllegalArgumentException("Please enter a positive number! Input: %s".formatted(value)); | ||
} | ||
return number; | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException("Please enter a valid number! Input: %s".formatted(value), e); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
retirement-calculator/src/main/java/com/craftsmanshipinsoftware/retirement/YearProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.craftsmanshipinsoftware.retirement; | ||
|
||
import java.time.Year; | ||
|
||
@FunctionalInterface | ||
interface YearProvider { | ||
|
||
Year currentYear(); | ||
} |
102 changes: 102 additions & 0 deletions
102
...ulator/src/test/java/com/craftsmanshipinsoftware/retirement/RetirementCalculatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package com.craftsmanshipinsoftware.retirement; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.PrintStream; | ||
import java.time.Year; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
class RetirementCalculatorTest { | ||
|
||
@Test | ||
void asksForCurrentAge() { | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()), | ||
new PrintStream(outputStream), Year::now); | ||
|
||
calculator.printYearsLeft(); | ||
|
||
assertThat(outputStream.toString()).contains("What is your current age?"); | ||
} | ||
|
||
@Test | ||
void asksForRetirementAge() { | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()), | ||
new PrintStream(outputStream), Year::now); | ||
|
||
calculator.printYearsLeft(); | ||
|
||
assertThat(outputStream.toString()).contains("At what age would you like to retire?"); | ||
} | ||
|
||
@Test | ||
void displaysYearsLeftUntilRetirement() { | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()), | ||
new PrintStream(outputStream), new FakeYearProvider()); | ||
|
||
calculator.printYearsLeft(); | ||
|
||
assertThat(outputStream.toString()).containsSequence("You have 40 years left until you can retire.\n", | ||
"It's 2015, so you can retire in 2055.\n"); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"65\n65", "75\n65"}) | ||
void displaysYouCanAlreadyRetireWhenCurrentAgeIsRetirementAgeOrMore(String input) { | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()), | ||
new PrintStream(outputStream), Year::now); | ||
|
||
calculator.printYearsLeft(); | ||
|
||
assertThat(outputStream.toString()).contains("You can already retire."); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"\n65", "25\n"}) | ||
void inputIsRequired(String input) { | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()), | ||
new PrintStream(new ByteArrayOutputStream()), Year::now); | ||
|
||
assertThatIllegalArgumentException() | ||
.isThrownBy(calculator::printYearsLeft) | ||
.withMessage("Input must not be empty!"); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"abc\n65", "25\nabc"}) | ||
void inputMustBeANumber(String input) { | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()), | ||
new PrintStream(new ByteArrayOutputStream()), Year::now); | ||
|
||
assertThatIllegalArgumentException() | ||
.isThrownBy(calculator::printYearsLeft) | ||
.withMessage("Please enter a valid number! Input: abc"); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"-1\n65", "25\n-1"}) | ||
void inputMustBeAPositiveNumber(String input) { | ||
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()), | ||
new PrintStream(new ByteArrayOutputStream()), Year::now); | ||
|
||
assertThatIllegalArgumentException() | ||
.isThrownBy(calculator::printYearsLeft) | ||
.withMessage("Please enter a positive number! Input: -1"); | ||
} | ||
|
||
static final class FakeYearProvider implements YearProvider { | ||
|
||
@Override | ||
public Year currentYear() { | ||
return Year.of(2015); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,9 @@ | ||
rootProject.name = 'exercises-for-programmers-java' | ||
include('saying-hello', 'characters-count', 'printing-quotes', 'mad-lib', 'simple-math') | ||
include( | ||
'saying-hello', | ||
'characters-count', | ||
'printing-quotes', | ||
'mad-lib', | ||
'simple-math', | ||
'retirement-calculator' | ||
) |