Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

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?

like image 785
Taha Yavuz Bodur Avatar asked Jan 08 '20 17:01

Taha Yavuz Bodur


2 Answers

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.

like image 194
Jean-Baptiste Yunès Avatar answered Oct 18 '22 09:10

Jean-Baptiste Yunès


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);
    }
}
like image 28
Naman Avatar answered Oct 18 '22 08:10

Naman