de.team33.patterns.decision.telesto.Choice Maven / Gradle / Ivy
Show all versions of decision-telesto Show documentation
package de.team33.patterns.decision.telesto;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A functional implementation of a simple decision.
*
* Typically used to implement multiple choices, e.g. based on an enum:
*
* public enum Result {
*
* A, B, C, D, E;
*
* public static Result map(final X x, final Y y, final Z z) {
* return Case.HEAD.apply(new Input(x, y, z));
* }
*
* private enum Case implements Function<Input, Result> {
*
* CASE_11_(Choice.on(Input::k3).reply(A).orReply(C)),
* CASE_10_(Choice.on(Input::k3).reply(B).orReply(E)),
* CASE_01_(Choice.on(Input::k3).reply(D).orReply(A)),
* CASE_00_(Choice.on(Input::k3).reply(D).orReply(B)),
* CASE_1__(Choice.on(Input::k2).apply(CASE_11_).orApply(CASE_10_)),
* CASE_0__(Choice.on(Input::k2).apply(CASE_01_).orApply(CASE_00_)),
* HEAD(Choice.on(Input::k1).apply(CASE_1__).orApply(CASE_0__));
*
* private final Function<Input, Result> backing;
*
* Case(Function<Input, Result> backing) {
* this.backing = backing;
* }
*
* @Override
* public Result apply(Input input) {
* return backing.apply(input);
* }
* }
*
* private static class Input {
*
* final X x;
* final Y y;
* final Z z;
*
* Input(final X x, final Y y, final Z z) {
* this.x = x;
* this.y = y;
* this.z = z;
* }
*
* final boolean k1() {
* return x.k1();
* }
*
* final boolean k2() {
* return y.k2();
* }
*
* final boolean k3() {
* return z.k3();
* }
* }
* }
*
*
* @param The parameter type
* @param The result type
* @see de.team33.patterns.decision.telesto package
*/
public class Choice implements Function
{
private final Predicate
condition;
private final Function
positive;
private final Function
negative;
private Choice(final Predicate
condition, final Function
positive, final Function
negative) {
this.condition = condition;
this.positive = positive;
this.negative = negative;
}
/**
* Creates and returns a {@link Conditional} based on a given {@link Predicate condition}.
* The first stage to finally build a {@link Choice}.
*/
public static
Conditional
on(final Predicate
condition) {
return new Conditional
(condition);
}
@Override
public final R apply(final P parameter) {
return (condition.test(parameter) ? positive : negative).apply(parameter);
}
/**
* Represents the second stage to finally build a {@link Choice}.
*
* It "knows" the condition as well as its positive reaction and allows defining a negative reaction
* in case the condition is false.
*/
@FunctionalInterface
public interface Consequence
{
/**
* Finally creates and returns a {@link Choice} based on a given {@link Function negative reaction}.
*/
Choice
orApply(Function
negative);
/**
* Finally creates and returns a {@link Choice} based on a given fixed value presumed as negative result.
*/
default Choice
orReply(final R negative) {
return orApply(any -> negative);
}
}
/**
* Represents the first stage to finally build a {@link Choice}.
*
* It "knows" the condition and allows defining a consequence in case the condition is true.
*/
public static class Conditional
{
private final Predicate
condition;
private Conditional(final Predicate
condition) {
this.condition = condition;
}
/**
* Creates and returns a {@link Consequence} based on a given {@link Function positive reaction}.
* The second stage to finally build a {@link Choice}.
*/
public final Consequence apply(final Function
positive) {
return negative -> new Choice<>(condition, positive, negative);
}
/**
* Creates and returns a {@link Consequence} based on a given fixed value presumed as positive result.
* The second stage to finally build a {@link Choice}.
*/
public final Consequence reply(final R positive) {
return apply(any -> positive);
}
}
}