Los desarrolladores siempre prefieren desarrollar una aplicación de Android mediante la aplicación de un patrón de arquitectura de software. Un patrón de arquitectura da modularidad a los archivos del proyecto y asegura que todos los códigos se cubran en las pruebas unitarias. Facilita a los desarrolladores la tarea de mantener el software y ampliar las características de la aplicación en el futuro. Hay algunas arquitecturas que son muy populares entre los desarrolladores y una de ellas es el patrón Modelo-Vista-Controlador (MVC). El patrón MVC sugiere dividir el código en 3 componentes. Al crear la clase/archivo de la aplicación, el desarrollador debe categorizarlo en una de las siguientes tres capas:
- Modelo: este componente almacena los datos de la aplicación. No tiene conocimiento sobre la interfaz. El modelo es responsable de manejar la lógica del dominio (reglas comerciales del mundo real) y la comunicación con la base de datos y las capas de red.
- Vista: es la capa UI (interfaz de usuario) que contiene los componentes que son visibles en la pantalla. Además, proporciona la visualización de los datos almacenados en el Modelo y ofrece interacción con el usuario.
- Controlador: Este componente establece la relación entre la Vista y el Modelo. Contiene la lógica de la aplicación central y se informa del comportamiento del usuario y actualiza el Modelo según la necesidad.
A pesar de aplicar el esquema MVC para dar un diseño modular a la aplicación, las capas de código dependen unas de otras. En este patrón, tanto la vista como el controlador dependen del modelo. Son posibles múltiples enfoques para aplicar el patrón MVC en el proyecto:
- Enfoque 1: las actividades y los fragmentos pueden desempeñar la función de controlador y son responsables de actualizar la vista.
- Enfoque 2: use actividad o fragmentos como vistas y controlador, mientras que Model será una clase separada que no extiende ninguna clase de Android.
En la arquitectura MVC , el controlador actualiza los datos de la aplicación y View obtiene los datos. Dado que el componente Modelo está separado, podría probarse independientemente de la interfaz de usuario. Además, si la capa Vista respeta el principio de responsabilidad única, entonces su función es solo actualizar el Controlador para cada evento de usuario y solo mostrar datos del Modelo, sin implementar ninguna lógica comercial. En este caso, las pruebas de IU deberían ser suficientes para cubrir las funcionalidades de la Vista.
Ejemplo de arquitectura MVC
Para comprender más claramente la implementación del patrón de arquitectura MVC, aquí hay un ejemplo simple de una aplicación de Android. Esta aplicación tendrá 3 botones y cada uno de ellos muestra el recuento de cuántas veces el usuario ha hecho clic en ese botón en particular. Para desarrollar esta aplicación se ha separado el código de la siguiente manera:
- El controlador y la vista serán manejados por la actividad. Cada vez que el usuario hace clic en los botones, la actividad indica al Modelo que maneje las operaciones posteriores. La actividad actuará como observador .
- The Model will be a separate class that contains the data to be displayed. The operations on the data will be performed by functions of this class and after updating the values of the data this Observable class notifies the Observer(Activity) about the change.
A continuación se muestra la implementación completa paso a paso de esta aplicación de Android utilizando el patrón de arquitectura MVC:
Nota: Los siguientes pasos se realizan en Android Studio versión 4.0
Paso 1: Crear un nuevo proyecto
- Haga clic en Archivo, luego en Nuevo => Nuevo proyecto.
- Elija Actividad vacía
- Seleccionar idioma como Java/Kotlin
- Seleccione el SDK mínimo según su necesidad.
Paso 2: modificar el archivo String.xml
Todas las strings que se utilizan en la actividad se enumeran en este archivo.
XML
<resources> <string name="app_name">GfG | MVC Architecture</string> <string name="Heading">MVC Architecture Pattern</string> <string name="Text1">Button_1</string> <string name="count">Count:0</string> </resources>
Paso 3: trabajar con el archivo activity_main.xml
Abra el archivo activity_main.xml y agréguele 3 botones que mostrarán los valores de conteo según la cantidad de veces que el usuario hace clic en él. A continuación se muestra el código para diseñar un diseño de actividad adecuado.
XML
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#168BC34A" tools:context=".MainActivity" > <!-- Provided Linear layout for the activity. --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <!-- TextView to display heading of the activity. --> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:layout_marginBottom="60dp" android:fontFamily="@font/roboto" android:text="@string/Heading" android:textAlignment="center" android:textColor="@android:color/holo_green_dark" android:textSize="30sp" android:textStyle="bold" /> <!-- First Button of the activity. --> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="30dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <!-- Second Button of the activity. --> <Button android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="50dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <!-- Third Button of the activity. --> <Button android:id="@+id/button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="50dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" android:background="#4CAF50" android:fontFamily="@font/roboto" android:text="@string/count" android:textColor="@android:color/background_light" android:textSize="24sp" android:textStyle="bold" /> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" app:srcCompat="@drawable/banner" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Paso 4: Creando la clase Modelo
Cree una nueva clase llamada Modelo para separar todos los datos y sus operaciones. Esta clase no conocerá la existencia de View Class.
Java
import java.util.*; public class Model extends Observable { // declaring a list of integer private List<Integer> List; // constructor to initialize the list public Model(){ // reserving the space for list elements List = new ArrayList<Integer>(3); // adding elements into the list List.add(0); List.add(0); List.add(0); } // defining getter and setter functions // function to return appropriate count // value at correct index public int getValueAtIndex(final int the_index) throws IndexOutOfBoundsException{ return List.get(the_index); } // function to make changes in the activity button's // count value when user touch it public void setValueAtIndex(final int the_index) throws IndexOutOfBoundsException{ List.set(the_index,List.get(the_index) + 1); setChanged(); notifyObservers(); } }
Kotlin
import java.util.* import kotlin.collections.ArrayList class Model : Observable() { // declaring a list of integer val List: MutableList<Int> // constructor to initialize the list init { // reserving the space for list elements List = ArrayList(3) // adding elements into the list List.add(0) List.add(0) List.add(0) } // defining getter and setter functions // function to return appropriate count // value at correct index @Throws(IndexOutOfBoundsException::class) fun getValueAtIndex(the_index: Int): Int { return List[the_index] } // function to make changes in the activity button's // count value when user touch it @Throws(IndexOutOfBoundsException::class) fun setValueAtIndex(the_index: Int) { List[the_index] = List[the_index] + 1 setChanged() notifyObservers() } }
Paso 5: Defina las funcionalidades de View y Controller en el archivo MainActivity
Esta clase establecerá la relación entre Vista y Modelo. Los datos proporcionados por el Modelo serán utilizados por View y se realizarán los cambios oportunos en la actividad.
Java
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import java.util.Observable; import java.util.Observer; public class MainActivity extends AppCompatActivity implements Observer, View.OnClickListener { // creating object of Model class private Model myModel; // creating object of Button class private Button Button1; private Button Button2; private Button Button3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // creating relationship between the // observable Model and the // observer Activity myModel = new Model(); myModel.addObserver(this); // assigning button IDs to the objects Button1 = findViewById(R.id.button); Button2 = findViewById(R.id.button2); Button3 = findViewById(R.id.button3); // transfer the control to Onclick() method // when a button is clicked by passing // argument "this" Button1.setOnClickListener(this); Button2.setOnClickListener(this); Button3.setOnClickListener(this); } @Override // calling setValueAtIndex() method // by passing appropriate arguments // for different buttons public void onClick(View v) { switch(v.getId()){ case R.id.button: myModel.setValueAtIndex(0); break; case R.id.button2: myModel.setValueAtIndex(1); break; case R.id.button3: myModel.setValueAtIndex(2); break; } } @Override // function to update the view after // the values are modified by the model public void update(Observable arg0, Object arg1) { // changing text of the buttons // according to updated values Button1.setText("Count: "+myModel.getValueAtIndex(0)); Button2.setText("Count: "+myModel.getValueAtIndex(1)); Button3.setText("Count: "+myModel.getValueAtIndex(2)); } }
Kotlin
import android.os.Bundle import android.view.View import android.widget.Button import androidx.appcompat.app.AppCompatActivity import java.util.* class MainActivity : AppCompatActivity(), Observer, View.OnClickListener { // creating object of Model class var myModel: Model? = null // creating object of Button class var Button1: Button? = null var Button2: Button? = null var Button3: Button? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // creating relationship between the // observable Model and the // observer Activity myModel = Model() myModel!!.addObserver(this) // assigning button IDs to the objects Button1 = findViewById(R.id.button) Button2 = findViewById(R.id.button2) Button3 = findViewById(R.id.button3) // transfer the control to Onclick() method // when a button is clicked by passing // argument "this" Button1?.setOnClickListener(this) Button2?.setOnClickListener(this) Button3?.setOnClickListener(this) } // calling setValueAtIndex() method // by passing appropriate arguments // for different buttons override fun onClick(v: View) { when (v.id) { R.id.button -> myModel?.setValueAtIndex(0) R.id.button2 -> myModel?.setValueAtIndex(1) R.id.button3 -> myModel?.setValueAtIndex(2) } } // function to update the view after // the values are modified by the model override fun update(arg0: Observable, arg1: Any?) { // changing text of the buttons // according to updated values Button1!!.text = "Count: " + myModel!!.getValueAtIndex(0) Button2!!.text = "Count: " + myModel!!.getValueAtIndex(1) Button3!!.text = "Count: " + myModel!!.getValueAtIndex(2) } }
Producción
Ventajas del patrón de arquitectura MVC
- El patrón MVC aumenta la capacidad de prueba del código y facilita la implementación de nuevas funciones, ya que admite en gran medida la separación de preocupaciones.
- Es posible realizar pruebas unitarias de Modelo y Controlador, ya que no amplían ni utilizan ninguna clase de Android.
- Las funcionalidades de la Vista se pueden verificar a través de pruebas de IU si la Vista respeta el principio de responsabilidad única (actualizar el controlador y mostrar datos del modelo sin implementar la lógica del dominio)
Desventajas del patrón de arquitectura MVC
- Las capas de código dependen unas de otras incluso si MVC se aplica correctamente.
- Ningún parámetro para manejar la lógica de la interfaz de usuario, es decir, cómo mostrar los datos.
Publicación traducida automáticamente
Artículo escrito por RISHU_MISHRA y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA