Functional programming patterns in java.
Reference: https://www.youtube.com/watch?v=YnzisJh-ZNI
Reference: https://www.youtube.com/watch?v=e4MT_OguDKg&index=146
Reference: https://www.amazon.com/Modern-Java-Action-functional-programming/dp/1617293563
This repo is a mix of functional design patterns that we have seen in books or on the internet.
- try to design you API in a composable way (package: composable)
class ShoppingAPI { static Function<List<Item>, Cart> buy() { return Cart::new; } static Function<Cart, Order> order() { return Order::new; } static Function<Order, Delivery> deliver() { return Delivery::new; } static Function<List<Item>, Delivery> oneClickBuy() { return buy() .andThen(order()) .andThen(deliver()); } }
- other used classes are as simple as they can be:
@Value class Cart { ImmutableList<Item> items; Cart(List<Item> items) { this.items = ImmutableList.copyOf(items); } } @Value class Delivery { Order order; } @Value class Item { int id; } @Value class Order { Cart cart; }
- other used classes are as simple as they can be:
- it's often helpful to use currying(https://github.com/mtumilowicz/groovy-closure-currying)
and functional interfaces to design API
then we can easily implement conversion classes
@FunctionalInterface interface CurrableDoubleBinaryOperator extends DoubleBinaryOperator { default DoubleUnaryOperator rate(double u) { return t -> applyAsDouble(t, u); } }
class RateConverter implements CurrableDoubleBinaryOperator { @Override public double applyAsDouble(double value, double rate) { return value * rate; } static DoubleUnaryOperator milesToKmConverter() { return new RateConverter().rate(1.609); } static DoubleUnaryOperator celsiusToFahrenheitConverter() { return new RateConverter().rate(1.8).andThen(x -> x + 32); } }
- use tuples and know the stream API
and we want to:
@Value @Builder public class Customer { ImmutableList<Order> orders; ImmutableList<Expense> expenses; // ... methods } @Value @Builder class Expense { Year year; ImmutableSet<String> tags; Stream<String> getTagsStream() { return SetUtils.emptyIfNull(tags).stream(); } }
- find order with max price
Optional<Order> findOrderWithMaxPrice() { return ListUtils.emptyIfNull(orders).stream() .filter(Order::hasPrice) .max(comparing(Order::getPrice)); }
- find top3 orders by price
Triple<Order, Order, Order> findTop3OrdersByPrice() { return ListUtils.emptyIfNull(orders).stream() .filter(Order::hasPrice) .sorted(comparing(Order::getPrice, reverseOrder())) .limit(3) .collect(collectingAndThen(toList(), ListToTripleConverter::convert)); }
- construct an immutable map with (key, value) = (year, tags from that year)
ImmutableMap<Year, Set<String>> yearTagsExpensesMap() { return ListUtils.emptyIfNull(expenses).stream() .collect(collectingAndThen(groupingBy(Expense::getYear, flatMapping(Expense::getTagsStream, toSet())), ImmutableMap::copyOf) ); }
- find order with max price