En
el tutorial anterior, hablamos sobre el modelo Model View Presenter,
cómo se aplica en Android y cuáles son sus ventajas más importantes. En este tutorial, exploramos el patrón Model View Presenter con más
detalle implementándolo en una aplicación de Android.
En
el tutorial anterior, hablamos sobre el modelo Model View Presenter,
cómo se aplica en Android y cuáles son sus ventajas más importantes. En este tutorial, exploramos el patrón Model View Presenter con más
detalle implementándolo en una aplicación de Android.
En este
tutorial:
Construimos una aplicación simple usando el patrón MVP
Exploramos cómo implementar el patrón MVP en Android
Y discutimos
cómo superar algunas dificultades causadas por la arquitectura de
Android
1. Model View Presenter
El
patrón Model View Presenter es un patrón arquitectónico basado en el
patrón Model View Controller (MVC) que aumenta la separación de las
preocupaciones y facilita las pruebas unitarias. Crea tres capas, Model,
View y Presenter, cada una con una responsabilidad bien definida.
El Modelo contiene la lógica de negocio de la aplicación. Controla cómo se crean, almacenan y modifican los datos. View es una
interfaz pasiva que muestra datos y dirige las acciones del usuario al
presentador Presenter. El Presentador actúa como intermediario. Recupera datos del
modelo y lo muestra en View. También procesa las acciones de
usuario enviadas por la vista.
2. Planificación y configuración del
proyecto
Vamos a construir una aplicación de notas sencilla para ilustrar
MVP. La aplicación permite al usuario tomar notas, guardarlas en una
base de datos local y eliminar notas. Para que sea sencillo, la
aplicación tendrá una sola actividad.
En este tutorial, nos concentramos principalmente en la implementación del patrón MVP. Otras
funciones, como la creación de una base de datos SQLite, la
construcción de un DAO o el manejo de la interacción del usuario, se
saltan. Si necesita ayuda con cualquiera de estos temas, Envato Tuts+
tiene algunos excelentes tutoriales sobre estos temas.
Diagrama de acción
y capas MVP
Comencemos con la creación de una nueva nota. Si
rompemos esta acción en operaciones más pequeñas, entonces este es el
aspecto que tendría el flujo usando el patrón arquitectónico MVP:
El
usuario escribe una nota y hace clic en el botón añadir nota.
El
presentador crea un objeto Note con el texto introducido por el usuario
y le pide al modelo que lo inserte en la base de datos.
El modelo
inserta la nota en la base de datos e informa al presentador de que la
lista de notas ha cambiado.
El presentador borra el campo de texto y
le pide a la vista que actualice su lista para mostrar la nota recién
creada.
Interfaces MVP
Consideremos ahora las operaciones necesarias para lograr esta acción y
separarlas usando MVP. Para mantener los diversos objetos ligeramente
acoplados, la comunicación entre las capas toma lugar utilizando
interfaces. Necesitamos cuatro interfaces:
RequiredViewOps:
necesario Ver operaciones disponibles para Presenter
ProvidedPresenterOps: operaciones ofrecidas a View para comunicación con
Presenter
RequiredPresenterOps: requiere las operaciones de
Presenter disponibles para Model
ProvidedModelOps: operaciones
ofrecidas a Model para comunicarse con Presenter
3. Implementación de MVP en Android
Ahora que tenemos una idea de cómo los diversos métodos deben ser
organizados, podemos comenzar a crear nuestra aplicación. Simplificamos
la implementación concentrándonos únicamente en la acción para agregar
una nueva nota. Los archivos fuente de este tutorial están disponibles
en GitHub.
Utilizamos solamente una actividad Activity con un diseño que
incluye:
EditText para nuevas notas
Button para añadir una
nota
RecyclerView para listar todas las notas
Dos elementos
TextView y un botón Button dentro de un contenedor de
RecyclerView
Interfaces
Comencemos creando las interfaces. Para mantener
todo organizado, ponemos las interfaces dentro de un titular. Una vez
más, en este ejemplo nos centramos en la acción para agregar una nueva
nota.
* Operations offered to Model to communicate with Presenter
39
* Handles all data business logic.
40
*/
41
interfaceProvidedModelOps{
42
// Model operations permitted to Presenter
43
intgetNotesCount();
44
NotegetNote(intposition);
45
intinsertNote(Notenote);
46
booleanloadData();
47
}
48
}
Capa View
Ahora es el momento de crear las capas Model, View y Presenter. Dado
que MainActivity funcionará como View, debería implementar la interfaz
RequiredViewOps.
El presentador es el intermediario y necesita implementar dos
interfaces:
ProvidedPresenterOps para permitir llamadas desde la
vista
RequiredPresenterOps para recibir resultados del modelo
Preste
especial atención a la referencia de capa Ver. Necesitamos
usar WeakReference<MVP_Main.RequiredViewOps> ya que MainActivity
podría ser destruida en cualquier momento y queremos evitar pérdidas de
memoria. Además, la capa de modelo aún no se ha configurado. Lo hacemos más tarde cuando conectamos las capas MVP.
getView().showToast(makeToast("Cannot add a blank note!"));
137
}catch(NullPointerExceptione){
138
e.printStackTrace();
139
}
140
}
141
}
142
143
/**
144
* Creates a Note object with given text
145
* @param noteText String with Note text
146
* @return A Note object
147
*/
148
publicNotemakeNote(StringnoteText){
149
Notenote=newNote();
150
note.setText(noteText);
151
note.setDate(getDate());
152
returnnote;
153
154
}
155
}
Capa de modelo
La capa Modelo es responsable de manejar la lógica. Contiene
una ArrayList con las notas agregadas a la base de datos, una
referencia DAO para realizar operaciones de base de datos y una
referencia al presentador.
* Gets a specific note from notes list using its array position
44
* @param position Array position
45
* @return Note from list
46
*/
47
@Override
48
publicNotegetNote(intposition){
49
returnmNotes.get(position);
50
}
51
52
/**
53
* Get ArrayList size
54
* @return ArrayList size
55
*/
56
@Override
57
publicintgetNotesCount(){
58
if(mNotes!=null)
59
returnmNotes.size();
60
return0;
61
}
62
}
4. Atar todo junto
Con las capas MVP en su lugar, necesitamos instanciarlas e insertar las
referencias necesarias. Antes de hacerlo, tenemos que abordar algunos
problemas que están directamente relacionados con Android.
Instanciar las
capas
Debido a que Android no permite la creación de instancias de una
actividad Activity, la capa de vista se instanciará para nosotros. Somos
responsables de instanciar las capas del presentador y del modelo. Desafortunadamente, instanciar esas capas fuera de la Actividad Activity puede
ser problemática.
Se recomienda utilizar una forma de inyección de
dependencia para lograr esto. Dado que nuestro objetivo es concentrarnos
en la implementación del MVP, tomaremos un enfoque más fácil. Este no
es el mejor enfoque disponible, pero es el más fácil de entender. Discutiremos MVP y la inyección de la dependencia más adelante en esta
serie.
Instanciar el Presentador y el Modelo en la Actividad usando
variables locales
Configure RequiredViewOps y ProvidedModelOps en el
presentador
Configure RequiredPresenterOps en el modelo
Guardar
ProvidedPresenterOps como referencia para usar en la vista
1
/**
2
* Setup Model View Presenter pattern
3
*/
4
privatevoidsetupMVP(){
5
// Create the Presenter
6
MainPresenterpresenter=newMainPresenter(this);
7
// Create the Model
8
MainModelmodel=newMainModel(presenter);
9
// Set Presenter model
10
presenter.setModel(model);
11
// Set the Presenter as a interface
12
mPresenter=presenter;
13
}
Manejo de cambios de configuración
Otra cosa que debemos considerar es el ciclo de vida de la actividad. La
actividad de Android Activity podría ser destruida en cualquier momento y las
capas de Presenter y Model también podrían ser destruidas con ella. Necesitamos solucionar esto usando algún tipo de máquina de estado para
guardar el estado durante los cambios de configuración. También debemos
informar a las otras capas sobre el estado de la Actividad.
Para
lograr esto, utilizaremos una clase separada, StateMaintainer, que
contiene un fragmento que mantiene su estado y utiliza este fragmento
para guardar y recuperar nuestros objetos. Puedes echar un vistazo a la
implementación de esta clase en los archivos de origen de este
tutorial.
Necesitamos agregar un método onDestroy al Presentador y al
Modelo para informarles sobre el estado actual de la Actividad. También
debemos agregar un método setView al Presentador, el cual será
responsable de recibir una nueva referencia View de la Actividad
recreada.
El patrón MVP es capaz de resolver algunos problemas causados por la
arquitectura predeterminada de Android. Hace que su código sea fácil de
mantener y probar. La
adopción de MVP puede parecer difícil al principio, pero una vez que
entienda la lógica detrás de ella, todo el proceso es sencillo.
Ahora
puede crear su propia biblioteca MVP o utilizar una solución que ya está
disponible, como Mosby o simple-mvp. Ahora deberías entender mejor lo
que estas bibliotecas están haciendo detrás de las escenas.
Estamos casi
al final de nuestro viaje MVP. En
la tercera y última parte de esta serie, agregaremos las pruebas
unitarias a la mezcla y adaptaremos nuestro código para usar la
inyección de dependencia con Dagger. Espero verte allí.