##Interfaces for presenters in MVP are a waste of time!
It's been a long time since we started talking about MVP. Today, the discussion is about if creating an interface for the Presenter in MVP is needed.
This is the Model View Presenter pattern's schema:
In this schema the Model box is related to all the code needed to implement your business logic, the presenter is the class implementing the presentation logic and the view is an interface created to abstract the view implementation.
Why the view should be implemented with an interface in this pattern? Because we want to decouple the code from the view implementation. We want to abstract the framework used to write our presentation layer independently of any external dependency. We want to be able to change the view implementation easily if needed. We want to follow the dependency rule and to improve unit testability. Remember that to follow the dependency rule high level concepts like the presenter implementation can't depend on low level details like the view implementation.
Why the interface is needed to improve unit testability? Because to write a unit test all the code should be related to your domain and no external systems as a SDK or a framework.
Let's go to put an example related to a login screen implemented for Android:
/**
* Login use case. Given an email and password executes the login process.
*/
public class Login {
private LoginService loginService;
public Login(LoginService loginService) {
this.loginService = loginService;
}
public void performLogin(String email, String password, LoginCallback callback) {
boolean loginSuccess = loginService.performLogin(email, password);
if (loginSuccess) {
callback.onLoginSuccess();
} else {
callback.onLoginError();
}
}
}
/**
* LoginPresenter, where the presentation logic related to the login user interface is implemented.
*/
public class LoginPresenter {
private LoginView view;
private Login login;
public LoginPresenter(LoginView view, Login login) {
this.view = view;
this.login = login;
}
public void onLoginButtonPressed(String email, String password) {
if (!areUserCredentialsValid(email, password)) {
view.showInvalidCredentialsMessage();
return;
}
login.performLogin(email, password, new LoginCallback {
void onLoginSuccess() {
view.showLoginSuccessMessage();
}
void onLoginError() {
view.showNetworkErrorMessage();
}
});
}
}
/**
* Declares what the presenter can do with the view without generating coupling to the view implementation details.
*/
public interface LoginView {
void showLoginSuccessMessage()
void showInvalidCredentialsMessage()
void showNetworkErrorMessage()
}
public class LoginActivity extends Activity implements LoginView {
.........
}
Please don't pay attention to the code syntax. I've written this from the scratch and it's almost pseudocode.
Why the View interface is needed here? To be able to write a unit test replacing the view implementation with a test double. Why is this needed in the unit test context? Because you don't want to mock the Android SDK and use the LoginActivity inside your unit tests. Remember that if you write a tets where the Android SDK is part of the SUT this is not a unit test.
At this part of the implementation is clear. We need an interface to do not depend on the view implementation.
Some developers have decided to add also an interface in top of the presenter. If we follow the previous example the implementation could be like this:
public interface LoginPresenter {
void onLoginButtonPressed(String email, String password);
}
public class LoginPresenterImpl implements LoginPresenter {
....
}
or
public interface ILoginPresenter {
void onLoginButtonPressed(String email, String password);
}
public class LoginPresenter implements ILoginPresenter {
....
}
What's the problem with this extra interface? IMHO this interface is not needed and is just adding complexity and noise to the development. Why?
- Look at the class name. When the interface is not needed the names used become weird and don't add semantic to the code.
- That interface is the class we have to modify to add a new method when the presentation logic has a new path and then we have to also update the implementation. Even when we use modern IDEs this is a waste of time.
- The navigation in the code could be difficult to follow because when you are inside the Activity (the view implementation) and you want to navigate to the presenter the file where you are going to go next is the interface when most of the time you want to go to the implementation.
- The interface is not improving the project testability. The presenter class can be easily replaced with a test double using any mocking library or any hand made test doubles. We don't want to write a test using the activity as SUT and replacing the presenter with a test double.
So...what is the LoginPresenter interface adding here? Just noise :)
But.....when should we use an interface? Interfaces should be used when we have more than one implementation (In this case the presenter implementation is just one) or if we need to create a strong boundary between our code and a third party component like a framewokr or a SDK. Even without interfaces we could use composition to generate abstraction, but the usage of an interface in Java is easier :) So, if you have more than one implementation of the same thing or you want to generate a strong boundary, then, add an interface. If not.....do not add more code. Remember, the less code to maintain the better. Remember that the usage of interfaces is not the only way to decouple our code and generate abstraction
But...what if I want to decouple the view implementation from the presenter implementation? You don't need to do that. The view implementation is a low level detail and the presenter imlementation is a high level abstraction. Implementation details can be coupled to high level abstractions. You want to abstract your domain model from the framework where it's executed, but you don't want to abstract in the opposite direction. Trying to reduce the coupling between the view implementation and the presenter is just a waste of time.
I've created this gist to discuss about this topic, please feel free to add any comment using code examples if needed :)
Extra ball: If you are thinking in different testing strategies for Android and the presentation layer I wouldn't use a unit test to test the presentation logic replacing the view with a test double. I'd try to use an approach like the one described here where the SUT is the whole presentation layer and not just the presenter (the test doubles are used to replace the use case).
Estoy con @flipper83 en eso de que esto no es una conversación que aplique solo para los presenters. El hacer interfaces o no, creo que es una cuestión de gustos más que otra cosa. Está claro que hay momentos en las que aportan muchísimo valor y creo que nadie lo cuestiona (multiples implementaciones) pero para los casos en los que el valor es más dudoso, se me ocurren argumentos a favor y en contra de todos los puntos que se están comentando:
- Sobre la legibilidad
Tener la interfaz mejora la legibilidad, siempre y cuando los nombres de tus métodos estén bien elegidos, en el sentido de que ves todo lo importante del tirón y no tienes el ruido (los detalles de implementación) añadido de tener más código que el del propio contrato. Eso claro, si lo que quieres ver es que hace tu presenter (por centrarnos en el ejemplo) a grandes rasgos. Si solo te interesa ver lo que hace un método en concreto, la empeoran en el sentido de que estás a un click o atajo de teclado más de ver los detalles de implementación, pero no me parece una razón de peso para descartarlas.
- Sobre testabilidad
Si quieres aislar tu SUT, dependiendo del framework de mocking que utilices, el tener una implementación o una interfaz puede ser o no ser un problema (ya no debería serlo). Otra cosa es que no quieras aislar cieras partes de tu sistema y la interfaz no te aporte demasiado.
- Sobre las consideraciones a la hora de diseñar
Aquí rompo una lanza en favor de las interfaces, pero tampoco me parece una razón de peso para hacerlas. En la práctica, yo (Sergio Arroyo) cuando estoy definiendo una interfaz, dedico más tiempo a pensar en el método, en qué tiene que hacer y no cómo tiene que hacerlo. Me pongo el sombrero de pedir y me marco unos requisitos para definir mi contrato. Que sea intuitivo, que me de pistas de como acabó el resultado (devolviendo lo que tiene que devolver o lanzando lo que tenga que lanzar). Sé que no debería ser así, pero muchas veces cuando estoy implementando algo y me hace falta un método y lo tengo más a mano, como que no lo pienso tanto. Es un problema mío que no tiene por qué aplicar al resto del mundo.
Así que eso, yo considero la interfaz un lugar "sagrado" en el que meter solo lo justo y necesario y tener solo los métodos de un vistazo me gusta y me ayuda. Si bien es cierto, que eso también lo puedo tener viendo la estrucutra de la clase en cualquier IDE que se precie.
En resumen, me parece que no es una decisión tan sumamente relevante. Es cuestión de gustos, y lo que es mejor... es una decisión fácilmente revocable, al menos en teoría. Si te pasa como a @txusballesteros, eso de tener que hacer un
el problema not tiene pinta de que fuera por hacer interfaz o no hacerla, sino que teniais una clase con muchas responsabilidades. Habría que ver si eso se hubiera detectado antes con la interfaz o sin ella. A priori colocar la interfaz en medio debería ser un proceso completamente mecánico, incluso automatizable (En Intellij: refactor -> extract -> interface).
Luego también hay que tener muy en cuenta el entorno de trabajo. No es lo mismo currar con gente super-senior que entiende profundamente todos los conceptos y tiene una capacidad bestial de refactorizar que currar en un equipo gigante con rotación, donde no todo el mundo tiene la capacidad de entender las decisiones y se busca la homogeneidad sobre todas las cosas y es más fácil hacer una norma que sea "para todas las clases que no sean contenedores de datos, hay que hacer una interfaz". Este segundo entorno no es el ideal pero no todo el mundo está en posición de elegir.
Y dejando de filosofar y hablando de lo que yo hago:
Antes hacía interfaces para todo, con el tiempo he dejado de hacerlas para muchas cosas, bien porque en mi equipo nadie las hace o bien porque no les veo valor en ese momento.
Ala, menudo chorizo os he soltado para no decir nada :P