JetBrains Academy: Function composition - Kamil-Jankowski/Learning-JAVA GitHub Wiki

JetBrains Academy: Function composition

Composing predicates:

Write the disjunctAll method that accepts a list of IntPredicate's and returns a single IntPredicate. The result predicate is a disjunction of all input predicates.

If the input list is empty then the result predicate should return false for any integer value (always false).

/**
 * The method represents a disjunct operator for a list of predicates.
 * For an empty list it returns the always false predicate.
 */
public static IntPredicate disjunctAll(List<IntPredicate> predicates) {
    return predicates.stream()
                     .reduce(initialValue -> false, IntPredicate::or);
}

The chain of responsibility:

Overview:

The chain of responsibility pattern is a object-oriented design pattern that processes a request through a series of handlers (a chain). The request (or something else) is sent from one handler to another and processed by one or all of these handlers in the chain.

In more detail: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

Usually, to implement this design pattern follow classes and method are created:

  • a general handler is an abstract class or interface with a method handle/process/etc and a method to set a next handler
  • one or more concrete handlers that implement the general handler

Using functional style we can implement it more simple. Instead of writing all concrete handlers we can write lambda expressions (or method references). For setting next handler it's possible to use function composition in similar to functions way.

Problem:

There is a class Request(data: String) for representing some XML requests in a payment system. The class has a getter method getData() and a constructor with one argument (data). See below.

Typically, the value of data looks like shown below (in a human-readable format, in processing it's a single line string).

<request>
  <transaction>
    <type>payment</type>
    <sum>100000</sum>
    <order_id>e94dc619-6172-4ffe-aae8-63112c551570</order>
    <desc>We'd like to buy an elephant</desc>
  </transaction>
  <digest>CZVMYTgc3iiOdJjFP+6dhQ==</digest>
</request>

You should to write the chain of responsibility pattern in functional style for stage-by-stage request creating.

For this:

  1. write a functional interface RequestHandler with a single abstract method handle() and a default method; the first is needed for using lambda expressions and should accept a Request and returns new Request with changed data, the second is for combining several handlers into the one.
  2. create a commonRequestHandler combined from three existing handlers: wrapInTransactionTag, createDigest and wrapInRequestTag.

In the next step, you can read how the requests should be created. Keep in mind, all handlers with logic are already implemented and available for you. You need to write only a suitable functional interface and compose all existing handlers as functions into one commonRequestHandler to follow the described algorithm.

The algorithm for creating a XML request:

0) Initial request data looks like shown below (it contains only business data).

<type>payment</type><sum>100000</sum><order_id>e94dc619-6172-4ffe-aae8-63112c551570</order><desc>We'd like to buy an elephant</desc>

1) First, a tag ... is added as border of the data (data is wrapped in the tag). The result:

<transaction><type>payment</type><sum>100000</sum><order_id>e94dc619-6172-4ffe-aae8-63112c551570</order><desc>We'd like to buy an elephant</desc></transaction>

2) Then, a digest (MD5) is calculated for entire data created on the previous stage and added as a new tag. The result:

<transaction><type>payment</type><sum>100000</sum><order_id>e94dc619-6172-4ffe-aae8-63112c551570</order><desc>We'd like to buy an elephant</desc></transaction><digest>CZVMYTgc3iiOdJjFP+6dhQ==</digest>

3) After that, the data is wrapped in a tag ... as shown below. The request is completed.

<request><transaction><type>payment</type><sum>100000</sum><order_id>e94dc619-6172-4ffe-aae8-
63112c551570</order><desc>We'd like to buy an elephant</desc></transaction>
<digest>CZVMYTgc3iiOdJjFP+6dhQ==</digest></request>

So, if you have a request data as shown in stage 0, the algorithm should to generate the result as shown in stage 3.

Note, each stage is already implemented for you in a specific RequestHandler.

/**
 * It represents a handler and has two methods: one for handling requests and other for combining handlers
 */
@FunctionalInterface
interface RequestHandler {

    // write a method handle that accept request and returns new request here
    // it allows to use lambda expressions for creating handlers below

    abstract Request handle(Request request);
        
    // write a default method for combining this and other handler single one
    // the order of execution may be any but you need to consider it when composing handlers
    // the method may has any name

    default RequestHandler chain(RequestHandler after) {
        return (Request req) -> after.handle(this.handle(req));
    }
}

/**
 * Accepts a request and returns new request with data wrapped in the tag <transaction>...</transaction>
 */
final static RequestHandler wrapInTransactionTag =
        (req) -> new Request(String.format("<transaction>%s</transaction>", req.getData()));

/**
 * Accepts a request and returns a new request with calculated digest inside the tag <digest>...</digest>
 */
final static RequestHandler createDigest =
        (req) -> {
            String digest = "";
            try {
                final MessageDigest md5 = MessageDigest.getInstance("MD5");
                final byte[] digestBytes = md5.digest(req.getData().getBytes("UTF-8"));
                digest = new String(Base64.getEncoder().encode(digestBytes));
            } catch (Exception ignored) { }
            return new Request(req.getData() + String.format("<digest>%s</digest>", digest));
        };

/**
 * Accepts a request and returns a new request with data wrapped in the tag <request>...</request>
 */
final static RequestHandler wrapInRequestTag =
        (req) -> new Request(String.format("<request>%s</request>", req.getData()));

/**
 * It should represents a chain of responsibility combined from another handlers.
 * The format: commonRequestHandler = handler1.setSuccessor(handler2.setSuccessor(...))
 * The combining method setSuccessor may has another name
 */
 
    // write the combining of existing handlers here

final static RequestHandler commonRequestHandler = wrapInTransactionTag.chain(createDigest.chain(wrapInRequestTag));

/**
 * Immutable class for representing requests.
 * If you need to change the request data then create new request.
 */
static class Request {
    private final String data;

    public Request(String requestData) {
        this.data = requestData;
    }

    public String getData() {
        return data;
    }
}

⚠️ **GitHub.com Fallback** ⚠️