Skip to content

Commit 40c977a

Browse files
committed
Refactor: Apply SRP to Calorie Calculator application
Separated concerns into multiple classes to adhere to the Single Responsibility Principle: - UserData: New DTO to hold user input. - UserInputHandler: Handles all console input operations and basic input validation. - CalorieCalculator: (Formerly CalorieCalculationService) Now solely responsible for BMR and daily calorie needs calculations. Contains all business logic for these computations. - ResultDisplay: Manages formatting and display of calculation results to the console. - MainApp: Orchestrates the application flow, coordinating between the input, calculation, and display components. This refactoring improves modularity, testability, and maintainability of the codebase. The original CalorieCalculator class, which previously handled multiple responsibilities (UI, validation, calculation), has had its calculation logic extracted into the new, focused CalorieCalculator class, and its other responsibilities moved to UserInputHandler, ResultDisplay, and MainApp.
1 parent 7bcbe01 commit 40c977a

File tree

5 files changed

+179
-78
lines changed

5 files changed

+179
-78
lines changed

Calorie Calculator/CalorieCalculator.java

Lines changed: 18 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import java.util.Scanner;
1+
// Handles the business logic for calorie calculations
22

33
public class CalorieCalculator {
44

@@ -16,88 +16,27 @@ public class CalorieCalculator {
1616
private static final double MODERATE_MULTIPLIER = 1.55;
1717
private static final double ACTIVE_MULTIPLIER = 1.725;
1818

19-
public static void main(String[] args) {
20-
Scanner scanner = new Scanner(System.in);
21-
22-
// Prompt the user for information
23-
System.out.println("Calorie Calculator");
24-
25-
System.out.print("Enter your gender (M/F): ");
26-
String gender = scanner.nextLine().trim().toUpperCase();
27-
28-
if (!gender.equals("M") && !gender.equals("F")) {
29-
System.out.println("Invalid gender input. Please enter 'M' or 'F'.");
30-
return;
31-
}
32-
33-
System.out.print("Enter your age (in years): ");
34-
int age = getValidIntInput(scanner);
35-
36-
System.out.print("Enter your weight (in kilograms): ");
37-
double weight = getValidDoubleInput(scanner);
38-
39-
System.out.print("Enter your height (in centimeters): ");
40-
double height = getValidDoubleInput(scanner);
41-
42-
System.out.print("Enter your activity level (sedentary/moderate/active): ");
43-
String activityLevel = scanner.nextLine().trim().toLowerCase();
44-
45-
if (!isValidActivityLevel(activityLevel)) {
46-
System.out.println("Invalid activity level input. Please choose 'sedentary', 'moderate', or 'active'.");
47-
return;
48-
}
49-
50-
// Calculate BMR
51-
double bmr = calculateBMR(gender, age, weight, height);
52-
53-
// Calculate daily calorie needs based on activity level
54-
double calorieNeeds = calculateCalorieNeeds(bmr, activityLevel);
55-
56-
// Display the results
57-
System.out.printf("Your Basal Metabolic Rate (BMR) is: %.0f calories per day.\n", bmr);
58-
System.out.printf("Your estimated daily calorie needs are: %.0f calories per day.\n", calorieNeeds);
59-
60-
scanner.close();
61-
}
62-
63-
// Method to get a valid integer input from the user
64-
private static int getValidIntInput(Scanner scanner) {
65-
while (!scanner.hasNextInt()) {
66-
System.out.println("Invalid input. Please enter a valid integer.");
67-
scanner.next(); // Clear invalid input
68-
}
69-
return scanner.nextInt();
70-
}
71-
72-
// Method to get a valid double input from the user
73-
private static double getValidDoubleInput(Scanner scanner) {
74-
while (!scanner.hasNextDouble()) {
75-
System.out.println("Invalid input. Please enter a valid number.");
76-
scanner.next(); // Clear invalid input
77-
}
78-
return scanner.nextDouble();
79-
}
80-
81-
// Method to check if the activity level is valid
82-
private static boolean isValidActivityLevel(String activityLevel) {
83-
return activityLevel.equals("sedentary") || activityLevel.equals("moderate") || activityLevel.equals("active");
84-
}
85-
86-
// Method to calculate BMR
87-
private static double calculateBMR(String gender, int age, double weight, double height) {
19+
public double calculateBMR(UserData userData) {
8820
double bmr;
89-
if (gender.equals("M")) {
90-
bmr = MALE_BMR_CONSTANT + (MALE_WEIGHT_COEFFICIENT * weight) + (MALE_HEIGHT_COEFFICIENT * height) - (MALE_AGE_COEFFICIENT * age);
91-
} else {
92-
bmr = FEMALE_BMR_CONSTANT + (FEMALE_WEIGHT_COEFFICIENT * weight) + (FEMALE_HEIGHT_COEFFICIENT * height) - (FEMALE_AGE_COEFFICIENT * age);
21+
if (userData.getGender().equals("M")) {
22+
bmr = MALE_BMR_CONSTANT +
23+
(MALE_WEIGHT_COEFFICIENT * userData.getWeight()) +
24+
(MALE_HEIGHT_COEFFICIENT * userData.getHeight()) -
25+
(MALE_AGE_COEFFICIENT * userData.getAge());
26+
} else { // Assumes "F" as gender has been validated by input handler
27+
bmr = FEMALE_BMR_CONSTANT +
28+
(FEMALE_WEIGHT_COEFFICIENT * userData.getWeight()) +
29+
(FEMALE_HEIGHT_COEFFICIENT * userData.getHeight()) -
30+
(FEMALE_AGE_COEFFICIENT * userData.getAge());
9331
}
9432
return bmr;
9533
}
9634

97-
// Method to calculate calorie needs based on activity level
98-
private static double calculateCalorieNeeds(double bmr, String activityLevel) {
35+
public double calculateDailyCalorieNeeds(double bmr, String activityLevel) {
36+
// Domain validation for activityLevel could be here if not handled before
37+
// For this example, assuming activityLevel is already validated
9938
double calorieNeeds;
100-
switch (activityLevel) {
39+
switch (activityLevel.toLowerCase()) {
10140
case "sedentary":
10241
calorieNeeds = bmr * SEDENTARY_MULTIPLIER;
10342
break;
@@ -108,7 +47,8 @@ private static double calculateCalorieNeeds(double bmr, String activityLevel) {
10847
calorieNeeds = bmr * ACTIVE_MULTIPLIER;
10948
break;
11049
default:
111-
throw new IllegalArgumentException("Invalid activity level");
50+
// This case should ideally not be reached if input is validated properly
51+
throw new IllegalArgumentException("Invalid activity level: " + activityLevel);
11252
}
11353
return calorieNeeds;
11454
}

Calorie Calculator/MainApp.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Orchestrates the application flow
2+
3+
public class MainApp {
4+
public static void main(String[] args) {
5+
UserInputHandler inputHandler = new UserInputHandler();
6+
CalorieCalculator calculationService = new CalorieCalculator();
7+
ResultDisplay resultDisplay = new ResultDisplay();
8+
9+
// 1. Get user data
10+
UserData userData = inputHandler.gatherUserData();
11+
12+
// 2. Perform calculations
13+
double bmr = calculationService.calculateBMR(userData);
14+
double dailyCalorieNeeds = calculationService.calculateDailyCalorieNeeds(bmr, userData.getActivityLevel());
15+
16+
// 3. Display results
17+
resultDisplay.displayResults(bmr, dailyCalorieNeeds);
18+
19+
// Clean up resources
20+
inputHandler.closeScanner();
21+
}
22+
}

Calorie Calculator/ResultDisplay.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Responsible for displaying the calculation results
2+
3+
public class ResultDisplay {
4+
public void displayResults(double bmr, double calorieNeeds) {
5+
System.out.printf("Your Basal Metabolic Rate (BMR) is: %.0f calories per day.\n", bmr);
6+
System.out.printf("Your estimated daily calorie needs are: %.0f calories per day.\n", calorieNeeds);
7+
}
8+
}

Calorie Calculator/UserData.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Data Transfer Object to hold user's information
2+
3+
public class UserData {
4+
private String gender;
5+
private int age;
6+
private double weight;
7+
private double height;
8+
private String activityLevel;
9+
10+
// Constructor
11+
public UserData(String gender, int age, double weight, double height, String activityLevel) {
12+
this.gender = gender;
13+
this.age = age;
14+
this.weight = weight;
15+
this.height = height;
16+
this.activityLevel = activityLevel;
17+
}
18+
19+
// Getters
20+
public String getGender() {
21+
return gender;
22+
}
23+
24+
public int getAge() {
25+
return age;
26+
}
27+
28+
public double getWeight() {
29+
return weight;
30+
}
31+
32+
public double getHeight() {
33+
return height;
34+
}
35+
36+
public String getActivityLevel() {
37+
return activityLevel;
38+
}
39+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import java.util.Scanner;
2+
3+
// Handles user input and basic format validation
4+
public class UserInputHandler {
5+
private Scanner scanner;
6+
7+
public UserInputHandler() {
8+
this.scanner = new Scanner(System.in);
9+
}
10+
11+
public UserData gatherUserData() {
12+
System.out.println("Calorie Calculator");
13+
14+
String gender = getValidGenderInput();
15+
int age = getValidIntInput("Enter your age (in years): ");
16+
double weight = getValidDoubleInput("Enter your weight (in kilograms): ");
17+
double height = getValidDoubleInput("Enter your height (in centimeters): ");
18+
String activityLevel = getValidActivityLevelInput();
19+
20+
return new UserData(gender, age, weight, height, activityLevel);
21+
}
22+
23+
private String getValidGenderInput() {
24+
String gender;
25+
while (true) {
26+
System.out.print("Enter your gender (M/F): ");
27+
gender = scanner.nextLine().trim().toUpperCase();
28+
if (gender.equals("M") || gender.equals("F")) {
29+
break;
30+
}
31+
System.out.println("Invalid gender input. Please enter 'M' or 'F'.");
32+
}
33+
return gender;
34+
}
35+
36+
private int getValidIntInput(String prompt) {
37+
int value;
38+
while (true) {
39+
System.out.print(prompt);
40+
if (scanner.hasNextInt()) {
41+
value = scanner.nextInt();
42+
scanner.nextLine(); // Consume newline
43+
if (value > 0) { // Basic validation for age
44+
break;
45+
} else {
46+
System.out.println("Invalid input. Please enter a positive integer.");
47+
}
48+
} else {
49+
System.out.println("Invalid input. Please enter a valid integer.");
50+
scanner.nextLine(); // Clear invalid input
51+
}
52+
}
53+
return value;
54+
}
55+
56+
private double getValidDoubleInput(String prompt) {
57+
double value;
58+
while (true) {
59+
System.out.print(prompt);
60+
if (scanner.hasNextDouble()) {
61+
value = scanner.nextDouble();
62+
scanner.nextLine(); // Consume newline
63+
if (value > 0) { // Basic validation for weight/height
64+
break;
65+
} else {
66+
System.out.println("Invalid input. Please enter a positive number.");
67+
}
68+
} else {
69+
System.out.println("Invalid input. Please enter a valid number.");
70+
scanner.nextLine(); // Clear invalid input
71+
}
72+
}
73+
return value;
74+
}
75+
76+
private String getValidActivityLevelInput() {
77+
String activityLevel;
78+
while (true) {
79+
System.out.print("Enter your activity level (sedentary/moderate/active): ");
80+
activityLevel = scanner.nextLine().trim().toLowerCase();
81+
if (activityLevel.equals("sedentary") || activityLevel.equals("moderate") || activityLevel.equals("active")) {
82+
break;
83+
}
84+
System.out.println("Invalid activity level input. Please choose 'sedentary', 'moderate', or 'active'.");
85+
}
86+
return activityLevel;
87+
}
88+
89+
public void closeScanner() {
90+
scanner.close();
91+
}
92+
}

0 commit comments

Comments
 (0)