Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,56 @@

import java.util.function.Function;

import static com.amigoscode._6_functionalinterfaces._3_extending.CombinatorPattern.ValidationResult.*;


/**
* Exercise: Combinator Pattern
*
* <p>
* 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.
*
* <p>
* 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<Customer, ValidationResult> {
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<Customer, ValidationResult>.
// Add three static methods that return CustomerValidator:
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@

/**
* Exercise: Extending Functional Interfaces
*
* <p>
* You can create your own functional interfaces that extend existing ones.
* A Transformer<T> extends Function<T, T> so that input and output share the
* same type -- useful for chaining transformations on the same data type.
*
* <p>
* 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<T> extends Function<T, T> {

default Transformer<T> andThenTransform(Transformer<T> after) {
return t -> after.apply(this.apply(t));
}
}
// TODO: 1 - Create a @FunctionalInterface called Transformer<T> that
// extends Function<T, T>. It inherits the abstract method apply(T t)
// from Function, so you do NOT need to declare it again.
Expand All @@ -34,6 +40,13 @@ public class ExtendingInterfaces {

public static void main(String[] args) {

Transformer<String> trimmer = String::trim;
Transformer<String> lowerCaser = String::toLowerCase;

Transformer<String> cleanUp = trimmer.andThenTransform(lowerCaser);

System.out.println(cleanUp.apply("HELLO WORLD"));

// TODO: 3 - Create a Transformer<String> called 'trimmer' that trims
// whitespace from a string using String::trim or s -> s.trim().

Expand All @@ -52,6 +65,10 @@ public static void main(String[] args) {
" Alice ", "BOB", " Charlie ", " DIANA "
);

List<String> 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<String>.
// Print the cleaned-up list.
Expand Down