How do you rewrite the code which using generics and functionals in Java 8 and mixing oop and functional programming by using only object-oriented?
Asked Answered
B

2

5

I have come across with this design and I didn't fully grasp mixing object oriented programming with functional programming in Java 8.

I was interested in mixing both language paradigms and most of the tutorials in the internet are about simple, so I wasn't be able to find a sample about large-scale software design by mixing them. So with this sample case I think I had an opportunity to dig it for some cases of it.

Let me show the related parts of the code in this case. This code contains a generic FetchUtils class which implements a custom iterator, but for the sake of brevity I have removed some parts.

public class FetchUtils<R, MSGIN, MSGOUT> {
    public SomeClass<R> getSomething(MSGIN query,
                                Function<MSGIN, MSGOUT> queryFunc,
                                Function<MSGOUT, List<R>> getResultList) {

               //...
                        MSGOUT callResult = queryFunc.apply(query);
                        buffer = getResultList.apply(callResult);
               //...
               //return someThing;
            }
            //...    
}

In the client there are a Function defined and a lambda expression pointing a reference to getCustomer method of a class. Actual call from the client to above method which is using generic types is sending these functionals.

public class CustomerResponse {
   //...
   public List<Customer> getCustomer() {
        if (thing == null) {
            thing = new ArrayList<Customer>();
        }
        return this.customers;
    }
   //...
}

public class MyClient {

   //...

   @Autowired
   private FetchUtils<Customer, CustomerRequest, CustomerResponse> fetchUtils;

   //...

   public SomeClass<Customer> fetch() {

      //...

      Function<CustomerRequest, CustomerResponse> requestFunc = q -> {
            try {
                return myService.doSomething(q);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };

      CustomerRequest query = new CustomerRequest(/*...*/);

      return fetchUtils.getSomething(query,
                                     requestFunc,
                                     r -> r.getCustomer());

      //...
   }
   //...
}

How do you rewrite this code only using object oriented programming i.e. without passing higher order functions and only using dynamic dispatch or even without generics?

Would this design be possible without generics?

How type inference works here with these generic types and functionals?

Is this design a possible example of mixing functional and object oriented programming or how do you evaluate this design?

Beanfeast answered 8/1, 2020 at 17:13 Comment(5)
How do you rewrite this code only using object oriented programming i.e. without passing higher order functions and only using dynamic dispatch or even without generics? - um, why? Generics and the use of higher-order functions are generally ways to reuse code and make it more understandable. Devolving it like you seem to be suggesting will require lots of boilerplate, potentially error-prone code (especially without the use of generics).Scourings
It looks like someone really wanted to create a Solution™. It might be even a full fledged Inner Platform if the full code is more of the same.Silverware
The first question to ask is: "What is this supposed to do?". We don't know what these code pieces are supposed to do exactly. A design is there to solve a problem. Without knowing the spec, we can't rewrite the code - especially when so much of it is missing.Dynatron
Thank you for your comments. First of all, I have deliberately omitted the parts about custom iterator in the FetchUtils since I only wanted to focus on the design itself. What I was meant by saying rewrite that trying to find out is there a general term for this design or some pointers for further study and what and how it could achieved this abstraction when you compare it to other possible designs.Beanfeast
@Dynatron You are completely right that you need the requirements here, so now I think maybe it would better to include custom iterator part in the code. But here I can say that there is that iterator having overrided hasNext method and in it and uses that functionals after each next iteration. There is an other omitted part that implements a spliterator using that iterator and returns a stream. So the purpose in this code is that given a web service consumer or in general any function fetch something and process results by applying again given functionals.Beanfeast
M
6

How do you rewrite this code only using object oriented programming i.e. without passing higher order functions and only using dynamic dispatch or even without generics?

class FuncImpl implements Function<CustomerRequest, CustomerResponse> {
    public CustomerResponse apply(CustomerResquest q) {
        try {
            return myService.doSomething(q);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
Function<CustomerRequest, CustomerResponse> requestFunc = new FuncImpl();

class FuncImpl2 implements Function<CustomerResponse,List<Customer>> {
    public List<Customer> apply(CustomerResponse r) {
        return r.getCustomer();
    }
}

...
return fetchUtils.getSomething(query, requestFunc,new FuncImpl2());

Would this design be possible without generics?

Of course, enforce the concrete type everywhere. (See below type deduction and make it by hand...).

How type inference works here with these generic types and functionals?

Generics parameters are just type variables, so your example is very easy to understand.

requestFun has type Function<CustomerRequest, CustomerResponse> then compiler can easily deduce that MSGIN is CustomerRequest and MSGOUT CustomerResponse.

For r -> r.getCustomer() compiler already knows that MSGOUT is CustomerResponse then it looks at CustomerResponse type and find that getCustomer returns a List<Customer> thus the lambda has type Function<CustomerResponse,List<Customer>> and then R is List<Customer>

Is this design a possible example of mixing functional and object oriented programming or how do you evaluate this design?

Yes it is a good example.

Mesquite answered 1/4, 2020 at 14:34 Comment(0)
S
4

If my understanding of your question is correct, by higher-order functions you mean the Function that you have been passing to the getSomething method in your code.

One way of thinking around that could be to simply represent classes abstract assigning each one of them their defined role. In an example, the following code does the same evaluation as in the code-shared by you but assigning different roles to respective individual classes.

abstract class Transform<I, O> {
    abstract O queryFunction(I input);
}

abstract class Group<O, R> {
    abstract List<R> groupIntoResult(O output);
}

abstract class FetchUtil<R, I, O> {
    Group<O, R> group;
    Transform<I, O> transform;

    public SomeClass<R> getSomething(I input) {
        O output = transform.queryFunction(input);
        List<R> grouped = group.groupIntoResult(transform.queryFunction(input));
        return new SomeClass<>(grouped);
    }
}

No doubt you can transform these into an interface as well depending upon the usage of these APIs. That is where the initial code you had was closer to actually making use of a FunctionalInterface class named Function.


To answer further, these representations without being generic can be extended easily to provide a customer-specific implementation in the following manner

class Transformer extends Transform<CustomerRequest, CustomerResponse> {

    @AutoWired
    MySerice myService;

    @Override
    CustomerResponse queryFunction(CustomerRequest input) {
        try {
            return myService.doSomething(input);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

class Grouping extends Group<CustomerResponse, Customer> {
    @Override
    List<Customer> groupIntoResult(CustomerResponse output) {
        return output.getCustomer();
    }
}

Note the simplification of tasks for your client with the FetchUtil definition now. It only needs to provide the request object and you an bind the transforming and grouping implementation to the utility class you've written to turnaround with a response.

Your client code cuts short to eventually the following lines and you can notice how this off burdens the clients consuming your sevices:

public class MyClient {
    @AutoWired
    private FetchUtil<Customer, CustomerRequest, CustomerResponse> fetchUtil;
    // bind the above to appropriate Group and Transform implementation

    public SomeClass<Customer> fetch() {
        CustomerRequest query = new CustomerRequest(/*...*/);
        return fetchUtil.getSomething(query);
    }
}
Stodder answered 4/4, 2020 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.