GoF Design Patterns Reloaded With Java8
GoF Design Patterns Reloaded With Java8
https://github.com/forax/design-pattern-reloaded
Me, Myself and I
@FunctionalInterface
interface BinOp {
int apply(int val1, int val2);
}
Conceptually equivalent to
typedef BinOp = (int, int) int
TR Function<T,R> apply
int R IntFunction<R> apply
T int ToIntFunction applyAsInt
Foreword - Method Reference
The operator :: allows to reference an existing method
(static or not)
:: on a type
BinOp add = Integer::sum;
:: on a reference
ThreadLocalRandom r = ThreadLocalRandom.current();
BinOp randomValue = r::nextInt;
Live Coding in jshell (1/3)
-> interface BinOp {
>> int apply(int v1, int v2);
>> }
| Added interface BinOp
-> BinOp add = (a, b) -> a + b;
| Added variable add of type BinOp with initial value
$Lambda$1/13648335@6bdf28bb
-> add.apply(2, 3);
| Expression value is: 5
| assigned to temporary variable $1 of type int
-> add = (x, y) -> x y;
| Variable add has been assigned the value $Lambda$2/968514068@511baa65
-> add.apply(2, 3);
| Expression value is: -1
| assigned to temporary variable $2 of type int
Live Coding in jshell (2/3)
-> List<String> list = Arrays.asList("hello", "paris");
| Added variable list of type List<String> with initial value [hello, paris]
-> list.forEach(item -> System.out.println(item));
hello
paris
-> Consumer<Object> printer = item -> System.out.println(item);
| Added variable printer of type Consumer<Object> with initial value
$Lambda$4/2114889273@3d24753a
-> list.forEach(printer);
hello
paris
-> Files.list(Paths.get(".")).forEach(printer);
./jimage-extracted
./.project
...
Live Coding in jshell (3/3)
Side note:
Some GoF patterns do not respect these principles
A simple Logger
Structural
Behavioral
Creational
Structural Patterns
Adapter,
Bridge,
Decorator,
Composite,
Proxy,
Flyweight, etc
interface Command {
void perform();
}
Command command =
() - > System.out.println("hello command");
Iteration
2 kinds
Internal iteration (push == Observer)
External iteration (pull == Iterator)
List<String> list =
Internal:
list.forEach(item -> System.out.println(item));
External:
for(String item: list) {
System.out.println(item);
}
External iteration is harder to write
forEach is easier to write than an Iterator
public class ArrayList<E> implements Iterable<E> {
private E[] elementData;
private int size;
public void forEach(Consumer<E> consumer) {
for(int i = 0; i < size; i++) {
consumer.accept(elementData[i]); internal
}
}
public Iterator<E> iterator() {
return new Iterator<E>() {
private int i;
public boolean hasNext() { return i < size; } external
public E next() { return elementData[i++]; }
};
}
}
Internal Iteration is less powerful
in Java
No side effect on local variables allowed !
List<Double> list =
Internal: sum is not effectively final
double sum = 0;
list.forEach(value -> sum += value);
External:
for(double value: list) {
sum += value;
}
Sum values of a CSV ?
chatty() quiet()
quiet()
Chatty Quiet
chatty()
Consumer<String>
State interface
Logger
public interface Logger {
enum Level { ERROR, WARNING }
void error(String message);
void warning(String message); ChattyLogger QuietLogger
Logger quiet();
Logger chatty();
static Logger logger(Consumer<String> printer) {
return new ChattyLogger(printer);
}
}
class A { class B {
B b; A a;
A(B b) { B(A a) {
this.b = b; this.a = a;
} }
} }
class A { class B {
B b; A a;
class A { class B {
B b; A a;
Given a hierarchy
package java.util.function;
public interface Function<T, R> {
public R apply(T t);
public default <V> Function<V,R> compose(Function<V,T> f) {}
public default <V> Function<T,V> andThen(Function<R,V> f) {}
}
Destructured Visitor
public class Visitor<R> {
public <T> Visitor<R> when(
Class<T> type, Function<T, R> fun) { }
public R call(Object receiver) { }
}
Static Factory
Factory method
Same problem as template method
Singleton
Abstract Factory Who want a global ?
Builder
Monad ?
Instance Creation
public interface Vehicle { }
public class Car implements Vehicle {
public Car(Color color) { }
}
public class Moto implements Vehicle {
public Moto(Color color) { }
}
I want to create only either 5 red cars or 5 blue
motos ?
Instance Factory ?
public interface VehicleFactory {
public Vehicle create();
}
public List<Vehicle> create5(VehicleFactory factory) {
return IntStream.range(0,5)
.mapToObj(i -> factory.create())
.collect(Collectors.toList());
}
List<Vehicle> redCars =
create5(redCarFactory); package java.util.function;
List<Vehicle> blueMotos =
@FunctionalInterface
create5(blueMotoFactory); public interface Supplier<T> {
public T get();
}
Instance Factory ==
Partial application on constructors
public List<Vehicle> create5(Supplier<Vehicle> factory) {
return IntStream.range(0,5)
.mapToObj(i -> factory.get())
.collect(Collectors.toList());
}
public static <T, R> Supplier<R> partial(
Function<T, R> function, T value) {
return () -> function.apply(value);
}
Method reference on new + constructor
List<Vehicle> redCars =
create5(partial(Car::new, RED)));
List<Vehicle> blueMotos =
create5(partial(Moto::new, BLUE)));
Static Factory
public interface Vehicle {
public static Vehicle create(String name) {
switch(name) {
case "car":
return new Car();
case "moto": Quite ugly isn't it ?
return new Moto();
default:
throw ...
}
}
}
Abstract Factory
public class VehicleFactory {
public void register(String name,
Supplier<Vehicle> supplier) {
...
}
public Vehicle create(String name) {
...
}
}
User validate
Exception
Monad
Represent 2 (or more) states
has a unified value in order to
compose transformations
User
of
get
User Exception
Monad
public class Validator<T> {
public static <T> Validator<T> of(T t) {
...
}
public Validator<T> validate(Predicate<T> validation, String message) {
...
}
public T get() throws IllegalStateException {
...
}
}
package java.util.function;
public interface Function<T, R> {
public R apply(T t);
public default <V> Function<V,R> compose(Function<V,T> f) {}
public default <V> Function<T,V> andThen(Function<R,V> f) {}
}
Monad impl
public class Validator<T> {
public Validator<T> validate(Predicate<T> validation,
String message) {
...
}
public <U> Validator<T> validate(Function<T, U> projection,
Predicate<U> validation, String message) {
return validate(
projection.andThen(validation::test)::apply, message);
} Function<U,Boolean>
}
Predicate<T>
Gather validation errors
public class Validator<T> {
private final T t;
private final ArrayList<Throwable> throwables = new ArrayList<>();
public Validator<T> validate(Predicate<T> validation, String message) {
if (!validation.test(t)) {
throwables.add(new IllegalStateException(message));
}
return this;
}
public T get() throws IllegalStateException {
if (throwables.isEmpty()) { return t; }
IllegalStateException e = new IllegalStateException();
throwables.forEach(e::addSuppressed);
throw e;
}
}
Summary
A monad represents several states
as a unified value