Search code examples
androidkotlinandroid-mvp

Use Kotlin higher order functions in MVP


Normally with Java, we make a *Contract interface to handle interaction between View and Presenter, like:

MainActivity
As View

public class MainActivity extends Activity implements MainContract {

@Override
public void onCreate(Bundle b) {
   presenter.requestData();
}

@Override
public void showData(String data) {
   // Handle data ...
}

MainPresenter
As Presenter

public class MainPresenter {

   public void requestData() {
      contract.showData("data");
   }
}

MainContract
As interface

public interface MainContract {
   void showData(String data);
}

Since Kotlin has the feature "Higher order function", should we simply pass functions to handle interaction between view and presenter? It can be something like:

View:

presenter.requestData { data ->
   // Handle data
}

Presenter:

fun requestData(handler: (String) -> Unit) {
   handler("data")
}

I'm not asking about the possibility, I'm asking if it's a best practice or not.


Solution

  • The answer of this question is more related architectural decisions than technical limitations. You could implement the same thing in Java (even in Java7) with anonymous instances (granted it would have a lot more of boilerplate and would be harder to read).

    The idea in MVP for having a contract that the view implements is that each presenter knows how to fetch, manipulate and present to said contract. Potentially a view can implement multiple contracts and have multiple presenters. Also each presenter instance only works with one implementation of the contract, but you could have two instances serving two different implementations.

    If instead of each view conforming to the contracts of each presenter each call of the presenter takes a lambda sooner or later you end up facing a problems.

    For example, imagine a presenter that fetches data asynchronously and caches it in memory:

    1. The view calls the presenter method fetchData().
    2. The presenter calls showLoading() method of the contract.
    3. (some time passes)
    4. The presenter calls hideLoading(), and showData(data) methods of the contract.
    5. The user interacts and triggers fetchData() again
    6. The presenter calls showData() with the cached data

    In this case if we used lambdas instead of a contract, we would need to request two different lambdas in the same method: one for when it's cached and one for when it's not. We, also, are coupling the view implementation to the presenter, in the future another implementation of the presenter interface might not need this two lambdas because the logic have changed.

    It is important to remember that in MVP ideally both view and presenter communicate to each other though interfaces and in this case the presenter interface should not be influenced by the implementation details and just expose what actions they are able to do.