Inside Rosie - The Presentation Layer
It has been a long time since we started writing applications using the main principles of Clean Architecture. We have developed several of them following these principles and it was during the past year when we fully understood that “The only way to make the deadline - the only way to go fast - is to keep the code as clean as possible at all times.” Robert C. Martin in Clean Code: A Handbook of Agile Software Craftsmanship.
Taking this sentence as a mantra we decided to code a framework to develop Android applications following the Clean Architecture principles and adding some other useful patterns that are not described in the original paper. And here is the result: https://github.com/Karumi/Rosie/
After designing the global architecture we started working on the Rosie’s presentation layer. This is the part of the software where all the code related to the view details and the presentation logic should be implemented. To implement this layer we decided to use Model View Presenter, a pattern closely related to software with a strong user interface component, which matches the typical mobile app.
The main idea behind Model View Presenter is to decouple the view implementation from the domain layer adding a concept named Presenter where the presentation logic is implemented.
Each screen of our Rosie-based Android applications uses a Presenter as a collaborator and implements a View contract. The responsibilities associated to the Presenter are:
- - Decide when the data is loaded based on the presentation lifecycle.
- - Decide what is the part of the model used to retrieve data.
- - React to view events to stimulate the software if needed.
- - Validate input data.
- - Transform information if needed to avoid coupling the view implementation to the domain layer implementation.
The view inside this pattern typically uses a Java interface which must be implemented by classes written on top of the Android SDK, like Activities or Fragments. The model represents the domain layer, this is, all the classes used to implement the application business logic.
The main benefits of using this pattern are:
- - Low coupling between the view implementation and the model.
- - High cohesion in all the presentation layer.
- - Improves presentation logic testability from the unit test point of view.
- - Code reusability between different view implementations.
- - Improve the maintainability of the software by making it easy to change the UI implementation.
This benefits are the result of the abstraction we introduce when adding a component inside the presentation logic implementation extraneous to the Android SDK. At the same time this component also acts as an indirection to decouple the view from the model and the view implementation from the Presenter.
All this was obviously in our minds when we developed our Model View Presenter layer :) . Finally, to create a presenter using Rosie you have to follow these steps:
- - Create an interface declaring what methods the View will offer to the Presenter. You need to extend from RosiePresenter.View:
public interface View extends RosiePresenter.View {
void foo();
}
- - Create your own Presenter as a class extending from RosiePresenter. Templatize the class signature to indicate that the View this Presenter is going to use is the one you have declared in the previous step:
public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
//Override the presenter lifecycle methods as needed
protected void initialize() {/*...*/}
protected void update() {/*...*/}
protected void pause() {/*...*/}
protected void destroy() {/*...*/}
private void sampleMethod() {
View view = getView(); // Get the view linked to the presenter
view.foo();
}
}
- - Create your Activity or Fragment extending from RosieActivity or RosieFragment and declare the Presenter created in the previous step as a field. The Presenter has to be annotated using the @Presenter annotation:
public class SampleActivity extends RosieActivity implements SamplePresenter.View {
@Inject @Presenter SamplePresenter presenter;
@Override protected void onPreparePresenter() {/*...*/}
}
Extending from RosieActivity or RosieFragment is not mandatory. If your project is already extending from any other base Activity please review the class PresenterLifeCycleLinker and use it inside your activities or fragments. Rosie also includes Dagger as a dependency injector and that is why the example already includes the @Inject annotation, but the usage if Dagger is also optional.
There are four methods connected to the Activity and Fragment lifecycle methods that will be automatically invoked for you. These methods are related to the Activity or Fragment lifecycle and is the point where the Presenter developer will take the control to implement the application presentation logic. The project README fully describes the connection between the Activity/Fragment lifecycle with the Presenter lifecycle.
Now you are ready to start implementing your presentation logic inside the Presenter :). An example of a Presenter implementation using Rosie can be found here.
If you look closely into the Rosie Model View Presenter implementation you will see that there are some interesting points to review:
- - The presenter implementation is based on the usage of Java generics to be able to create a method
getView()
using the type of the view declared in the class signature:
public class RosiePresenter<T extends RosiePresenter.View> {
protected final T getView() {
return view;
}
}
public class CharactersPresenter extends MarvelPresenter<CharactersPresenter.View> {
}
-
- The Activity or Fragment lifecycle is automatically linked to the Presenter lifecycle using a class named PresenterLifecycleLinker used in RosieActivity and RosieFragment. Create your activities or fragments extending from RosieActivity or RosieFragment and add the @Presenter annotation to your Presenter declaration inside the Activity or Fragment in order to use your own RosiePresenter implementation easily. But, remember that extending from RosieActivity or RosieFragment is not mandatory, you can use the PresenterLifeCycleLinker if your activities already have a base class.
-
- All your Activities or Fragments using a Presenter will implement a View interface. This view must be an interface extending from RosiePresenter.View.
public class CharactersFragment extends MarvelFragment implements CharactersPresenter.View {
}
- - The instance of the view obtained by the RosiePresenter
getView
method will be automatically replaced by a fake implementation if the view is not in foreground. This is needed to avoid view updates when the Activity or the Fragment are not visible and to avoid repeating null checks for thegetView
return value. This implementation can be found inside RosiePresenter and it’s based on the Null Object Pattern. If you are familiar with the main mocking libraries implementations, this code should ring a bell.
/**
* Changes the current view instance with a dynamic proxy to avoid real UI updates.
*/
void resetView() {
final Class<?> viewClass = getViewInterfaceClass();
InvocationHandler emptyHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
};
ClassLoader classLoader = viewClass.getClassLoader();
Class[] interfaces = new Class[1];
interfaces[0] = viewClass;
this.view = (T) Proxy.newProxyInstance(classLoader, interfaces, emptyHandler);
}
private Class<?> getViewInterfaceClass() {
Class<?> interfaceClass = null;
Class<?>[] interfaces = this.view.getClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class<?> interfaceCandidate = interfaces[i];
if (RosiePresenter.View.class.isAssignableFrom(interfaceCandidate)) {
interfaceClass = interfaceCandidate;
}
}
return interfaceClass;
}
The connection with the model can be implemented using a method named createUseCaseCall
, but we will talk about this topic in an upcoming blog post explaining the use cases and threading model used to implement Rosie.
Using just this part of Rosie we can easily develop Android applications obtaining all the benefits of the Model View Presenter pattern described before.
See you soon!
References:
- - The Clean Architecture by Robert C. Martin - https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
- - GUI Architectures by Martin Fowler - http://martinfowler.com/eaaDev/uiArchs.html
- - Rosie - https://github.com/Karumi/Rosie
- - RosiePresenter class - https://github.com/Karumi/Rosie/blob/master/rosie/src/main/java/com/karumi/rosie/view/RosiePresenter.java
- - RosieActivity class - https://github.com/Karumi/Rosie/blob/master/rosie/src/main/java/com/karumi/rosie/view/RosieActivity.java
- - PresenterLifeCycleLinker class - https://github.com/Karumi/Rosie/blob/master/rosie/src/main/java/com/karumi/rosie/view/PresenterLifeCycleLinker.java