issue #335 Monad pattern introduced
This commit is contained in:
19
monad/src/main/java/com/iluwatar/monad/App.java
Normal file
19
monad/src/main/java/com/iluwatar/monad/App.java
Normal file
@ -0,0 +1,19 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
User user = new User("user", 24, Sex.FEMALE, "foobar.com");
|
||||
System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null")
|
||||
.validate(User::getName, name -> !name.isEmpty(), "name is empty")
|
||||
.validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'")
|
||||
.validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString());
|
||||
}
|
||||
}
|
5
monad/src/main/java/com/iluwatar/monad/Sex.java
Normal file
5
monad/src/main/java/com/iluwatar/monad/Sex.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
public enum Sex {
|
||||
MALE, FEMALE
|
||||
}
|
39
monad/src/main/java/com/iluwatar/monad/User.java
Normal file
39
monad/src/main/java/com/iluwatar/monad/User.java
Normal file
@ -0,0 +1,39 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private Sex sex;
|
||||
private String email;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - name
|
||||
* @param age - age
|
||||
* @param sex - sex
|
||||
* @param email - email
|
||||
*/
|
||||
public User(String name, int age, Sex sex, String email) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.sex = sex;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public Sex getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
}
|
81
monad/src/main/java/com/iluwatar/monad/Validator.java
Normal file
81
monad/src/main/java/com/iluwatar/monad/Validator.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Class representing Monad design pattern. Monad is a way of chaining operations on the
|
||||
* given object together step by step. In Validator each step results in either success or
|
||||
* failure indicator, giving a way of receiving each of them easily and finally getting
|
||||
* validated object or list of exceptions.
|
||||
* @param <T> Placeholder for an object.
|
||||
*/
|
||||
public class Validator<T> {
|
||||
private final T t;
|
||||
private final List<Throwable> exceptions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* @param t object to be validated
|
||||
*/
|
||||
private Validator(T t) {
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates validator against given object
|
||||
*
|
||||
* @param t object to be validated
|
||||
* @param <T> object's type
|
||||
* @return new instance of a validator
|
||||
*/
|
||||
public static <T> Validator<T> of(T t) {
|
||||
return new Validator<>(Objects.requireNonNull(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validation one argument boolean-valued function that
|
||||
* represents one step of validation. Adds exception to main validation exception
|
||||
* list when single step validation ends with failure.
|
||||
* @param message error message when object is invalid
|
||||
* @return this
|
||||
*/
|
||||
public Validator<T> validate(Predicate<T> validation, String message) {
|
||||
if (!validation.test(t)) {
|
||||
exceptions.add(new IllegalStateException(message));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for the {@link Validator#validate(Function, Predicate, String)} method,
|
||||
* dedicated for objects, that need to be projected before requested validation.
|
||||
* @param projection function that gets an objects, and returns projection representing
|
||||
* element to be validated.
|
||||
* @param validation see {@link Validator#validate(Function, Predicate, String)}
|
||||
* @param message see {@link Validator#validate(Function, Predicate, String)}
|
||||
* @param <U> see {@link Validator#validate(Function, Predicate, String)}
|
||||
* @return this
|
||||
*/
|
||||
public <U> Validator<T> validate(Function<T, U> projection, Predicate<U> validation,
|
||||
String message) {
|
||||
return validate(projection.andThen(validation::test)::apply, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* To receive validated object.
|
||||
*
|
||||
* @return object that was validated
|
||||
* @throws IllegalStateException when any validation step results with failure
|
||||
*/
|
||||
public T get() throws IllegalStateException {
|
||||
if (exceptions.isEmpty()) {
|
||||
return t;
|
||||
}
|
||||
IllegalStateException e = new IllegalStateException();
|
||||
exceptions.forEach(e::addSuppressed);
|
||||
throw e;
|
||||
}
|
||||
}
|
13
monad/src/test/java/com/iluwatar/monad/AppTest.java
Normal file
13
monad/src/test/java/com/iluwatar/monad/AppTest.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AppTest {
|
||||
|
||||
@Test
|
||||
public void testMain() {
|
||||
String[] args = {};
|
||||
App.main(args);
|
||||
}
|
||||
|
||||
}
|
42
monad/src/test/java/com/iluwatar/monad/MonadTest.java
Normal file
42
monad/src/test/java/com/iluwatar/monad/MonadTest.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.iluwatar.monad;
|
||||
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class MonadTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testForInvalidName() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
User tom = new User(null, 21, Sex.MALE, "tom@foo.bar");
|
||||
Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null").get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForInvalidAge() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
User tom = new User("John", 17, Sex.MALE, "john@qwe.bar");
|
||||
Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null")
|
||||
.validate(User::getAge, age -> age > 21, "user is underaged")
|
||||
.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForValid() {
|
||||
User tom = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org");
|
||||
User validated = Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null")
|
||||
.validate(User::getAge, age -> age > 21, "user is underaged")
|
||||
.validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female")
|
||||
.validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign")
|
||||
.get();
|
||||
Assert.assertSame(validated, tom);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user