From 70b34db27a0cc6fc1e825175f514cc52b7993522 Mon Sep 17 00:00:00 2001 From: Ephraim Gibson Date: Mon, 7 Sep 2026 06:32:05 +0300 Subject: [PATCH] practiced combinator pattern exercises --- .../_3_extending/CombinatorPattern.java | 62 +++++++++++++++---- .../_3_extending/ExtendingInterfaces.java | 21 ++++++- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/CombinatorPattern.java b/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/CombinatorPattern.java index b9a607e..de4f34a 100644 --- a/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/CombinatorPattern.java +++ b/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/CombinatorPattern.java @@ -2,30 +2,56 @@ import java.util.function.Function; +import static com.amigoscode._6_functionalinterfaces._3_extending.CombinatorPattern.ValidationResult.*; + + /** * Exercise: Combinator Pattern - * + *

* The Combinator Pattern uses functional interfaces to build complex validation * logic by combining small, reusable validation functions. Each validator is a * function that takes an object and returns a validation result. Validators can * be chained using an and() method to create comprehensive validation pipelines. - * + *

* This pattern is powerful because: - * - Each validator is a simple, testable unit - * - Validators are composable (combine them freely) - * - New validators can be added without modifying existing code + * - Each validator is a simple, testable unit + * - Validators are composable (combine them freely) + * - New validators can be added without modifying existing code */ public class CombinatorPattern { - // TODO: 1 - Create a ValidationResult enum with values: - // SUCCESS, EMAIL_NOT_VALID, NOT_ADULT, NAME_EMPTY - - - // TODO: 2 - Create a Customer record (or class) with three fields: - // String name, String email, int age - // Hint for record: record Customer(String name, String email, int age) {} + enum ValidationResult { + SUCCESS, + EMAIL_NOT_VALID, + NOT_ADULT, + NAME_EMPTY + } + record Customer(String name, String email, int age) { + } + @FunctionalInterface + interface CustomerValidator extends Function { + static CustomerValidator isEmailValid() { + return (customer) -> customer.email.contains("@") ? SUCCESS : EMAIL_NOT_VALID; + } + + static CustomerValidator isAdult() { + return (customer) -> customer.age >= 18 ? SUCCESS : NOT_ADULT; + } + + static CustomerValidator isNameNotEmpty() { + return customer -> customer.name == null || customer.name.isBlank() + ? NAME_EMPTY : ValidationResult.SUCCESS; + } + + default CustomerValidator and(CustomerValidator other) { + return customer -> { + ValidationResult result = this.apply(customer); + return result == SUCCESS ? other.apply(customer) : result; + }; + } + } // TODO: 3 - Create a @FunctionalInterface called CustomerValidator that // extends Function. // Add three static methods that return CustomerValidator: @@ -67,10 +93,22 @@ public static void main(String[] args) { // .and(CustomerValidator.isNameNotEmpty()); + CustomerValidator fullValidator = CustomerValidator.isEmailValid().and(CustomerValidator.isAdult()).and(CustomerValidator.isNameNotEmpty()); + // TODO: 6 - Create a valid customer ("Alice", "alice@example.com", 25) // and validate using fullValidator. Print the result. // Expected: SUCCESS + Customer alice = new Customer("Alice", "alice@example.com", 25); + Customer bob = new Customer("Bob", "bob-no-email", 30); + Customer young = new Customer("", "young@email.com", 16); + Customer unknown = new Customer("", "valid@email.com", 25); + + + System.out.println(fullValidator.apply(alice)); + System.out.println(fullValidator.apply(bob)); + System.out.println(fullValidator.apply(young)); + System.out.println(fullValidator.apply(unknown)); // TODO: 7 - Create and validate these invalid customers, printing each result: // a) ("Bob", "bob-no-email", 30) -> Expected: EMAIL_NOT_VALID diff --git a/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/ExtendingInterfaces.java b/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/ExtendingInterfaces.java index f7c16e0..08c233d 100644 --- a/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/ExtendingInterfaces.java +++ b/src/main/java/com/amigoscode/_6_functionalinterfaces/_3_extending/ExtendingInterfaces.java @@ -7,16 +7,22 @@ /** * Exercise: Extending Functional Interfaces - * + *

* You can create your own functional interfaces that extend existing ones. * A Transformer extends Function so that input and output share the * same type -- useful for chaining transformations on the same data type. - * + *

* You will also add default methods to your functional interface, which is * allowed as long as there is still exactly one abstract method. */ public class ExtendingInterfaces { + interface Transformer extends Function { + + default Transformer andThenTransform(Transformer after) { + return t -> after.apply(this.apply(t)); + } + } // TODO: 1 - Create a @FunctionalInterface called Transformer that // extends Function. It inherits the abstract method apply(T t) // from Function, so you do NOT need to declare it again. @@ -34,6 +40,13 @@ public class ExtendingInterfaces { public static void main(String[] args) { + Transformer trimmer = String::trim; + Transformer lowerCaser = String::toLowerCase; + + Transformer cleanUp = trimmer.andThenTransform(lowerCaser); + + System.out.println(cleanUp.apply("HELLO WORLD")); + // TODO: 3 - Create a Transformer called 'trimmer' that trims // whitespace from a string using String::trim or s -> s.trim(). @@ -52,6 +65,10 @@ public static void main(String[] args) { " Alice ", "BOB", " Charlie ", " DIANA " ); + List cleanUpList = messyStrings.stream().map(cleanUp).toList(); + + cleanUpList.forEach(System.out::println); + // TODO: 6 - Apply the 'cleanUp' transformer to each element of // messyStrings and collect the results into a new List. // Print the cleaned-up list.