From 1da33f28284207fd3d6b2357c48510dfc60fadb3 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 18 Dec 2025 22:26:22 -0500 Subject: [PATCH 1/4] Added Java code for single responsibility --- .../java/bad/KeywordOrchestrator.java | 50 ++++++++++++++++++ .../java/good/KeywordOrchestrator.java | 51 +++++++++++++++++++ .../java/good/KeywordStats.java | 32 ++++++++++++ .../java/good/services/CsvFileParser.java | 17 +++++++ .../java/good/services/KeywordValidator.java | 14 +++++ .../good/services/SearchVolumeService.java | 15 ++++++ .../java/good/services/StatsRepository.java | 14 +++++ 7 files changed, 193 insertions(+) create mode 100644 case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/KeywordStats.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java create mode 100644 case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java diff --git a/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java b/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java new file mode 100644 index 0000000..aa6eabb --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java @@ -0,0 +1,50 @@ +import java.util.List; +import java.util.Objects; + +/** + * This class is an example of violation of the Single-Responsibility principle + * of the SOLID principles suite + * + * @author Arthur Tristram, Software Engineer (Java) + * @since 2025-12-18 + */ +public class KeywordOrchestrator { + + private static final String filePath = "keywords.csv"; + + /** + * @param filePath path of the file to be read + * @return void + * @implNote performs all actions in a single method (Violates SR) + */ + public void processKeywords(String filePath) { + System.out.printf("Opening file at %s...\n", filePath); + // 1. Reading File Logic (Mixed responsibility) + + List rawData = List.of("buy shoes", "", "best running shoes", "shoes"); + + for (String row : rawData) { + // 2. Validation Logic (Mixed responsibility) + if (Objects.isNull(row) || row.length() == 0) { + continue; + } + + // 3. API Logic (Mixed responsibility) + System.out.printf("Checking API for: %s\n", row); + double volume = Math.floor(Math.random() * 1000); // Fake API call + + // 4. Database Logic (Mixed responsibility) + System.out.printf("INSERT INTO keyword_stats (term, volume) VALUES('%s', %.0f)\n", row, volume); + } + } + // Usage + /** + * @param args any command line parameters that may be passed + * @return void + * @implNote calls the monolithic processKeywords() function + */ + public static void main(String[] args) { + KeywordOrchestrator keywordOrchestrator = new KeywordOrchestrator(); + keywordOrchestrator.processKeywords(filePath); + } +} diff --git a/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java b/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java new file mode 100644 index 0000000..57dc8a8 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java @@ -0,0 +1,51 @@ +import java.util.List; + +/** + * @author Arthur Tristram, Software Engineer (Java) + * @since 2025-12-18 + * @implNote This class calls Single Responsibility services as advised by SOLID + * principles(mimics microservices) + */ +public class KeywordOrchestrator { + private CsvFileParser parser; + private KeywordValidator validator; + private SearchVolumeService api; + private StatsRepository repo; + + private static final String filePath = "data.csv"; + + public KeywordOrchestrator(CsvFileParser parser, KeywordValidator validator, SearchVolumeService api, + StatsRepository repo) { + this.parser = parser; + this.validator = validator; + this.api = api; + this.repo = repo; + } + + /** + * @param filePath + * @implNote The run service that calls various microservices to execute business logic + */ + public void run(String filePath) { + List keywords = parser.read(filePath); + + for (String keyword : keywords) { + if (!validator.isValid(keyword)) + continue; + + double volume = api.getVolume(keyword); + repo.save(new KeywordStats(keyword, volume)); + } + } + + /** + * @param args any arguments passed into the command line + * @implNote Instantiates the class with the various services and calls the run() service + */ + public static void main(String[] args) { + KeywordOrchestrator keywordOrchestrator = new KeywordOrchestrator(new CsvFileParser(), new KeywordValidator(), + new SearchVolumeService(), new StatsRepository()); + + keywordOrchestrator.run(filePath); + } +} diff --git a/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java b/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java new file mode 100644 index 0000000..498b642 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java @@ -0,0 +1,32 @@ +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-18 + * @implNote Model class for database objects + */ +public class KeywordStats { + private String term; + private double volume; + + public KeywordStats(String term, double volume) { + this.term = term; + this.volume = volume; + } + + // Getters & Setters for encapsulation + public String getTerm() { + return term; + } + + public void setTerm(String term) { + this.term = term; + } + + public double getVolume() { + return volume; + } + + public void setVolume(double volume) { + this.volume = volume; + } + +} diff --git a/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java b/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java new file mode 100644 index 0000000..a047b48 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java @@ -0,0 +1,17 @@ +import java.util.List; + +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-18 + * @implNote This class handles the responsibility of reading the file + */ +public class CsvFileParser { + /** + * @param fileName name of the file to be parsed + * @return a list containing file records + */ + public List read(String fileName) { + System.out.printf("Reading file: %s\n", fileName); + return List.of("buy shoes", "", "best running shoes", "shoes"); + } +} diff --git a/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java b/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java new file mode 100644 index 0000000..3dda715 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java @@ -0,0 +1,14 @@ +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-18 + * @implNote This class handles the responsibility of validating the file + */ +public class KeywordValidator { + /** + * @param term the string to be validated + * @return boolean true if valid, false if invalid + */ + public boolean isValid(String term) { + return term.length() > 0; + } +} diff --git a/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java b/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java new file mode 100644 index 0000000..e9bb968 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java @@ -0,0 +1,15 @@ +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-18 + * @implNote This class handles the responsibility of reading the file + */ +public class SearchVolumeService { + /** + * @param term the record to be processed + * @return volume generated by a random API call + */ + public double getVolume(String term) { + System.out.printf("[API] Fetching volume for: %s\n", term); + return Math.floor(Math.random() * 1000); + } +} diff --git a/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java b/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java new file mode 100644 index 0000000..918ef35 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java @@ -0,0 +1,14 @@ +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-18 + * @implNote This class handles the responsibility of saving object to repository + */ +public class StatsRepository { + /** + * @param stats the database object to be saved + * @return void + */ + public void save(KeywordStats stats) { + System.out.printf("[DB] Saving: %s -> %.0f\n", stats.getTerm(), stats.getVolume()); + } +} From 33aa4c6fd3e792ef9a8ca2cb09875ab4f005a085 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 18 Dec 2025 22:38:02 -0500 Subject: [PATCH 2/4] Added a README for the java project --- .../01-srp-keyword-processor/java/README.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 case-studies/01-srp-keyword-processor/java/README.md diff --git a/case-studies/01-srp-keyword-processor/java/README.md b/case-studies/01-srp-keyword-processor/java/README.md new file mode 100644 index 0000000..3aa7352 --- /dev/null +++ b/case-studies/01-srp-keyword-processor/java/README.md @@ -0,0 +1,53 @@ +# Java Implementation: SRP Keyword Processor + +## Prerequisites + +- Java Development Kit (JDK) 21 or higher +- Command-line access (Terminal, PowerShell, etc.) + +## Project Structure + +- `bad/KeywordOrchestrator.java`: Example that violates the Single Responsibility Principle (SRP) +- `good/KeywordOrchestrator.java` and related classes: Example that follows SRP using separate service classes + +## How to Compile and Run + +### 1. Compile the Code + +Navigate to the `java` directory: + +```bash +cd case-studies/01-srp-keyword-processor/java +``` + +Compile all Java files (recommended): + +```bash +javac bad/*.java good/*.java +``` + +### 2. Run the "Bad" Version + +This version violates SRP by combining all logic in one class. + +```bash +java bad.KeywordOrchestrator +``` + +### 3. Run the "Good" Version + +This version follows SRP by delegating responsibilities to separate classes. + +```bash +java good.KeywordOrchestrator +``` + +## Notes + +- Ensure you are using JDK 21 or higher for compatibility. +- No external libraries are required; only standard Java is used. +- Output will be printed to the console, demonstrating the difference in design approaches. + +--- + +For more information on the Single Responsibility Principle and SOLID, see the project documentation or comments in the source files. \ No newline at end of file From b521c2c344f8096176433e494a21cce09bc07859 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 18 Dec 2025 23:35:35 -0500 Subject: [PATCH 3/4] Added package name and updated README to update run instructions --- .../01-srp-keyword-processor/java/README.md | 17 ++++++++++++----- .../java/bad/KeywordOrchestrator.java | 14 ++++++++------ .../java/good/KeywordOrchestrator.java | 10 ++++++++-- .../java/good/KeywordStats.java | 1 + .../java/good/services/CsvFileParser.java | 3 ++- .../java/good/services/KeywordValidator.java | 3 ++- .../java/good/services/SearchVolumeService.java | 3 ++- .../java/good/services/StatsRepository.java | 4 ++++ 8 files changed, 39 insertions(+), 16 deletions(-) diff --git a/case-studies/01-srp-keyword-processor/java/README.md b/case-studies/01-srp-keyword-processor/java/README.md index 3aa7352..87aa7fd 100644 --- a/case-studies/01-srp-keyword-processor/java/README.md +++ b/case-studies/01-srp-keyword-processor/java/README.md @@ -11,8 +11,9 @@ - `good/KeywordOrchestrator.java` and related classes: Example that follows SRP using separate service classes ## How to Compile and Run +If you're using VSCode and have the Java extension, just open good/KeywordOrchestrator.java or bad version and click 'run' that appears above the main method. Other IDEs will have different processes. If you want to use the command line, do the following: -### 1. Compile the Code +### 1. Compile the Code to a Build Directory Navigate to the `java` directory: @@ -20,10 +21,16 @@ Navigate to the `java` directory: cd case-studies/01-srp-keyword-processor/java ``` -Compile all Java files (recommended): +Create a build directory (if it doesn't exist): ```bash -javac bad/*.java good/*.java +mkdir build +``` + +Compile all Java files to the build directory: + +```bash +javac -d build bad/*.java good/*.java ``` ### 2. Run the "Bad" Version @@ -31,7 +38,7 @@ javac bad/*.java good/*.java This version violates SRP by combining all logic in one class. ```bash -java bad.KeywordOrchestrator +java -cp build bad.KeywordOrchestrator ``` ### 3. Run the "Good" Version @@ -39,7 +46,7 @@ java bad.KeywordOrchestrator This version follows SRP by delegating responsibilities to separate classes. ```bash -java good.KeywordOrchestrator +java -cp build good.KeywordOrchestrator ``` ## Notes diff --git a/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java b/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java index aa6eabb..36671e7 100644 --- a/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java +++ b/case-studies/01-srp-keyword-processor/java/bad/KeywordOrchestrator.java @@ -1,3 +1,5 @@ +package bad; + import java.util.List; import java.util.Objects; @@ -10,7 +12,7 @@ */ public class KeywordOrchestrator { - private static final String filePath = "keywords.csv"; + private static final String FILE_PATH = "keywords.csv"; /** * @param filePath path of the file to be read @@ -18,23 +20,23 @@ public class KeywordOrchestrator { * @implNote performs all actions in a single method (Violates SR) */ public void processKeywords(String filePath) { - System.out.printf("Opening file at %s...\n", filePath); + System.out.printf("Opening file at %s...%n", filePath); // 1. Reading File Logic (Mixed responsibility) List rawData = List.of("buy shoes", "", "best running shoes", "shoes"); for (String row : rawData) { // 2. Validation Logic (Mixed responsibility) - if (Objects.isNull(row) || row.length() == 0) { + if (Objects.isNull(row) || row.isBlank()) { continue; } // 3. API Logic (Mixed responsibility) - System.out.printf("Checking API for: %s\n", row); + System.out.printf("Checking API for: %s%n", row); double volume = Math.floor(Math.random() * 1000); // Fake API call // 4. Database Logic (Mixed responsibility) - System.out.printf("INSERT INTO keyword_stats (term, volume) VALUES('%s', %.0f)\n", row, volume); + System.out.printf("INSERT INTO keyword_stats (term, volume) VALUES('%s', %.0f)%n", row, volume); } } // Usage @@ -45,6 +47,6 @@ public void processKeywords(String filePath) { */ public static void main(String[] args) { KeywordOrchestrator keywordOrchestrator = new KeywordOrchestrator(); - keywordOrchestrator.processKeywords(filePath); + keywordOrchestrator.processKeywords(FILE_PATH); } } diff --git a/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java b/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java index 57dc8a8..1c57e0c 100644 --- a/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java +++ b/case-studies/01-srp-keyword-processor/java/good/KeywordOrchestrator.java @@ -1,5 +1,11 @@ +package good; import java.util.List; +import good.services.CsvFileParser; +import good.services.KeywordValidator; +import good.services.SearchVolumeService; +import good.services.StatsRepository; + /** * @author Arthur Tristram, Software Engineer (Java) * @since 2025-12-18 @@ -12,7 +18,7 @@ public class KeywordOrchestrator { private SearchVolumeService api; private StatsRepository repo; - private static final String filePath = "data.csv"; + private static final String FILE_PATH = "data.csv"; public KeywordOrchestrator(CsvFileParser parser, KeywordValidator validator, SearchVolumeService api, StatsRepository repo) { @@ -46,6 +52,6 @@ public static void main(String[] args) { KeywordOrchestrator keywordOrchestrator = new KeywordOrchestrator(new CsvFileParser(), new KeywordValidator(), new SearchVolumeService(), new StatsRepository()); - keywordOrchestrator.run(filePath); + keywordOrchestrator.run(FILE_PATH); } } diff --git a/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java b/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java index 498b642..c540032 100644 --- a/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java +++ b/case-studies/01-srp-keyword-processor/java/good/KeywordStats.java @@ -1,3 +1,4 @@ +package good; /** * @author Arthur Tristram, Software Engineer(Java) * @since 2025-12-18 diff --git a/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java b/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java index a047b48..e7beb7c 100644 --- a/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java +++ b/case-studies/01-srp-keyword-processor/java/good/services/CsvFileParser.java @@ -1,3 +1,4 @@ +package good.services; import java.util.List; /** @@ -11,7 +12,7 @@ public class CsvFileParser { * @return a list containing file records */ public List read(String fileName) { - System.out.printf("Reading file: %s\n", fileName); + System.out.printf("Reading file: %s%n", fileName); return List.of("buy shoes", "", "best running shoes", "shoes"); } } diff --git a/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java b/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java index 3dda715..d52056d 100644 --- a/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java +++ b/case-studies/01-srp-keyword-processor/java/good/services/KeywordValidator.java @@ -1,3 +1,4 @@ +package good.services; /** * @author Arthur Tristram, Software Engineer(Java) * @since 2025-12-18 @@ -9,6 +10,6 @@ public class KeywordValidator { * @return boolean true if valid, false if invalid */ public boolean isValid(String term) { - return term.length() > 0; + return !term.isEmpty(); } } diff --git a/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java b/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java index e9bb968..d9eded0 100644 --- a/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java +++ b/case-studies/01-srp-keyword-processor/java/good/services/SearchVolumeService.java @@ -1,3 +1,4 @@ +package good.services; /** * @author Arthur Tristram, Software Engineer(Java) * @since 2025-12-18 @@ -9,7 +10,7 @@ public class SearchVolumeService { * @return volume generated by a random API call */ public double getVolume(String term) { - System.out.printf("[API] Fetching volume for: %s\n", term); + System.out.printf("[API] Fetching volume for: %s%n", term); return Math.floor(Math.random() * 1000); } } diff --git a/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java b/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java index 918ef35..a07bd5b 100644 --- a/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java +++ b/case-studies/01-srp-keyword-processor/java/good/services/StatsRepository.java @@ -1,3 +1,7 @@ +package good.services; + +import good.KeywordStats; + /** * @author Arthur Tristram, Software Engineer(Java) * @since 2025-12-18 From 77b47185794a9fb582c595f7827e1a26573cd0cf Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 19 Dec 2025 21:27:08 -0500 Subject: [PATCH 4/4] Added java code for 02-ocp --- .../02-ocp-payroll-system/java/README.md | 60 +++++++++ .../java/src/bad/SalaryCalculator.java | 72 ++++++++++ .../java/src/good/SalaryCalculator.java | 125 ++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 case-studies/02-ocp-payroll-system/java/README.md create mode 100644 case-studies/02-ocp-payroll-system/java/src/bad/SalaryCalculator.java create mode 100644 case-studies/02-ocp-payroll-system/java/src/good/SalaryCalculator.java diff --git a/case-studies/02-ocp-payroll-system/java/README.md b/case-studies/02-ocp-payroll-system/java/README.md new file mode 100644 index 0000000..be3de59 --- /dev/null +++ b/case-studies/02-ocp-payroll-system/java/README.md @@ -0,0 +1,60 @@ +# Java Implementation + +## Prerequisites + +- Java Development Kit (JDK) 21 or higher +- Command-line access (Terminal, PowerShell, etc.) + +## Project Structure + +- `src/bad/SalaryCalculator.java`: Example that violates the Open-Closed Principle (OCP) +- `src/good/SalaryCalculator.java` and related classes: Example that follows OCP using inheritance and polymorphism + +## How to Compile and Run +If you're using VSCode and have the Java extension, just open the good or bad SalaryCalculator.java and click 'run' above the main method. Other IDEs will have different processes. If you want to use the command line, do the following: + +### 1. Compile the Code + +Navigate to the `java` directory: + +```bash +cd case-studies/02-ocp-payroll-system/java +``` + +Create a build directory (if it doesn't exist): + +```bash +mkdir build +``` + +Compile all Java files to the build directory: + +```bash +javac -d build src/bad/SalaryCalculator.java src/good/SalaryCalculator.java +``` + +### 2. Run the "Bad" Version + +This version violates OCP by using conditional logic for all employee types. + +```bash +java -cp build bad.SalaryCalculator +``` + +### 3. Run the "Good" Version + +This version follows OCP by using inheritance and polymorphism. + +```bash +java -cp build good.SalaryCalculator +``` + +## Notes + +- Ensure you are using JDK 21 or higher for compatibility. +- No external libraries are required; only standard Java is used. +- Output will be printed to the console, demonstrating the difference in design approaches. + +--- + +For more information on the Open-Closed Principle and SOLID, see the project documentation or comments in the source files. \ No newline at end of file diff --git a/case-studies/02-ocp-payroll-system/java/src/bad/SalaryCalculator.java b/case-studies/02-ocp-payroll-system/java/src/bad/SalaryCalculator.java new file mode 100644 index 0000000..9b68a78 --- /dev/null +++ b/case-studies/02-ocp-payroll-system/java/src/bad/SalaryCalculator.java @@ -0,0 +1,72 @@ +package bad; + +import java.util.List; + +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-19 + * @implNote This module demonstrates violation of the Open-Closed principle + * which states that a class must be closed for modification but open + * for extension + */ +enum EmployeeType { + FULL_TIME, CONTRACTOR, INTERN; +} + +class Employee { + String name; + EmployeeType type; + double hourlyRate; + double hoursWorked; + + public Employee(String name, EmployeeType type, double hourlyRate, double hoursWorked) { + this.name = name; + this.type = type; + this.hourlyRate = hourlyRate; + this.hoursWorked = hoursWorked; + } +} + +/** + * The universal class that does everyhting through conditional logic + * This keeps the class open for modification as a different use case would + * require yet another conditional check in the same class + */ +public class SalaryCalculator { + /** + * @param employee The employee for which salary is to be computed + * @return double The computed salaary + */ + public double calculateSalary(Employee employee) { + if (employee.type.toString().equals("FULL_TIME")) { + // Regular hourly rate + 20% bonus + return employee.hourlyRate * employee.hoursWorked * 1.2; + } else if (employee.type.toString().equals("CONTRACTOR")) { + // Flat hourly rate, no bonus + return employee.hourlyRate * employee.hoursWorked; + } else if (employee.type.toString().equals("INTERN")) { + // Hourly rate but capped at 80% + return employee.hourlyRate * employee.hoursWorked * 0.8; + } + return 0; + } + + // Usage + /** + * @param args Any command line parameters passed from the command line + * @return void + * @implNote Creates a list of employees and calculates the salaries for each of + * them + */ + public static void main(String[] args) { + List Employees = List.of(new Employee("Alice", EmployeeType.FULL_TIME, 50, 160), + new Employee("Bob", EmployeeType.CONTRACTOR, 60, 100), + new Employee("Charlie", EmployeeType.INTERN, 20, 80)); + + SalaryCalculator calculator = new SalaryCalculator(); + + for (Employee emp : Employees) { + System.out.printf("%s (%s): $%.0f\n", emp.name, emp.type, calculator.calculateSalary(emp)); + } + } +} \ No newline at end of file diff --git a/case-studies/02-ocp-payroll-system/java/src/good/SalaryCalculator.java b/case-studies/02-ocp-payroll-system/java/src/good/SalaryCalculator.java new file mode 100644 index 0000000..da47812 --- /dev/null +++ b/case-studies/02-ocp-payroll-system/java/src/good/SalaryCalculator.java @@ -0,0 +1,125 @@ +package good; + +import java.util.List; + +/** + * @author Arthur Tristram, Software Engineer(Java) + * @since 2025-12-19 + * @implNote This module demonstrates the practice of the Open-Closed principle + * which states that classes must be open for extension but closed for + * modification. By making employee abstract, we can add more specific + * use cases by simply using inheritence + */ + +abstract class Employee { + String name; + + abstract double calculateSalary(); +} + +class FullTimeEmployee extends Employee { + + double hourlyRate; + double hoursWorked; + + FullTimeEmployee(String name, double hourlyRate, double hoursWorked) { + this.name = name; + this.hourlyRate = hourlyRate; + this.hoursWorked = hoursWorked; + } + + @Override + double calculateSalary() { + // Regular hourly rate + 20% bonus + return this.hourlyRate * this.hoursWorked * 1.2; + } +} + +class Contractor extends Employee { + + double hourlyRate; + double hoursWorked; + + Contractor(String name, double hourlyRate, double hoursWorked) { + this.name = name; + this.hourlyRate = hourlyRate; + this.hoursWorked = hoursWorked; + } + + @Override + double calculateSalary() { + // Flat hourly rate, no bonus + return this.hourlyRate * this.hoursWorked; + } +} + +class Intern extends Employee { + + double hourlyRate; + double hoursWorked; + + Intern(String name, double hourlyRate, double hoursWorked) { + this.name = name; + this.hourlyRate = hourlyRate; + this.hoursWorked = hoursWorked; + } + + @Override + double calculateSalary() { + // Hourly rate but capped at 80% + return this.hourlyRate * this.hoursWorked * 0.8; + } +} + +// --- EXTENSION --- +// If we want to add a "Freelancer", we just create a new class. +// We DO NOT touch the existing classes or the calculator. +class Freelancer extends Employee { + + double fixedProjectFee; + + Freelancer(String name, double fixedProjectFee) { + this.name = name; + this.fixedProjectFee = fixedProjectFee; + } + + @Override + double calculateSalary() { + return this.fixedProjectFee; + } +} + +public class SalaryCalculator { + /** + * @param employees + * @return double The combined total of all employees' salaries + */ + public static double calculateTotalSalaries(List employees) { + double total = 0; + for (Employee emp : employees) { + double salary = emp.calculateSalary(); + System.out.printf("%s: $%.0f\n", emp.name, salary); + total += salary; + } + return total; + } + + /** + * @param args and arguments passed through the command line + * @return void + * @implNote Creates a list of employees polymorphically and calculates the + * total of their salaries + */ + public static void main(String[] args) { + + List employees = List.of( + new FullTimeEmployee("Alice", 50, 160), + new Contractor("Bob", 60, 100), + new Intern("Charlie", 20, 80), + new Freelancer("Dave", 5000)); // New type added easily! + + System.out.println("--- Payroll Report ---"); + double total = calculateTotalSalaries(employees); + System.out.printf("Total Payroll: $%.0f\n", total); + } +}