Patrón de arquitectura MVC (controlador de vista de modelo) en Android con ejemplo

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.

MVC (Model—View—Controller) Architecture Pattern in Android

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

  1. Haga clic en Archivo, luego en Nuevo => Nuevo proyecto.
  2. Elija Actividad vacía
  3. Seleccionar idioma como Java/Kotlin
  4. 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *