fbpx
Get In Touch
1201 3rd Avenue Seattle, WA 98101, US
(HQ) Av. Punto Sur 31, Tlajomulco de Zúñiga, Jal 45050, MX
Carrera 11B # 99 - 25, Btá, 110221, CO
Let's talk
hello@inmediatum.com
Ph: +1 (650) 603 0883
Sales attention M - F 9am - 5pm (CT)
Get support
Careers
Endless inspiration and meaningful work
See open positions
Back

MVVM – Qué es y como funciona.

¡Qué tal! Eduardo Rodríguez de este lado del monitor para hablarles un poco acerca de MVVM, que es y como funcionan, pero antes no olvides pasarte por mi articulo anterior en el cual te explico acerca de JTW.

Un poco de contexto

Todos los desarrolladores de software estamos en búsqueda del código perfecto. Lo cierto es que nunca es posible lograrlo, ya que por muchas razones, a veces externas, a veces por nuestra limitación de conocimiento o simplemente porque nuestra manera de codificar siempre está en constante evolución (o al menos debería), es imposible obtenerlo. De igual manera, otra cosa que es cierta es que siempre podemos intentarlo, siempre podemos tratar de aprender más, de usar técnicas para que sea más escalable, más fácil de leer, más eficiente, etc. Una de estas tantas maneras de hacer mejor nuestro código es utilizar patrones de diseño.

¿Que es Patron de diseño?

Por definición, los patrones de diseño son:

“Técnicas para resolver problemas comunes en el desarrollo de software y otros ámbitos referentes al diseño de interacción o interfaces.”

Existen muchos patrones de diseño, algunos se puede utilizar combinados entre y su uso dependerá del problema que queramos resolver con nuestro software.

Si bien la finalidad de este articulo no es explicar a fondo que son patrones de diseño, la idea es que tengan una base de que son, ya que en este caso hablaremos específicamente de uno enfocado a Android.

¿Que es MVVM?

MVVM, por sus siglas en ingles Model View ViewModel, es un patrón de diseño que tiene por finalidad separar la parte de la interfaz del usuario(de ahí la V de View) de la parte de la lógica del negocio(de ahí la M de Model), logrando así que la parte visual sea totalmente independiente. El otro componente es el ViewModel que es la parte que va a interactuar como puente entre la Vista y el Modelo.

¿Cómo aplica MVVM en Android?

Aqui se muestra graficamente como es que se implementa el MVVM en Android:

Android nos da una colección de librerías que nos pueden ser muy útiles a la hora de querer mejorar el desarrollo de nuestras apps llamadas Componentes de la arquitectura, en este caso nos centraremos en dos de las clases que estas librerías nos ofrecen

  • ViewModel

Esta clase será el intermediario entre nuestra vista y nuestra lógica del negocio, es la encargada de almacenar la información de la interfaz gráfica, ya que una de sus ventajas más grandes es que no se destruye en el cambio de orientación de nuestra aplicación.

  • LiveData

Esta clase nos sirve para compilar objetos de datos que nos permitirán notificar cuando algún valor sea modificado por la parte de la lógica del negocio, logrando con esto que la interfaz se entere y haga los ajustes necesarios.

 

Veamos algo de código

Lo que el siguiente código realiza es básicamente un login utilizando el patrón de diseño MVVM, veamos las clases que lo componen:

Modelos de datos:

Empecemos por un par de modelos que seran necesarios para poder llevar acabo nuestra accion de login, uno sera el modelo de la respuesta para hacer saber a la interfaz en que paso esta y otro sera el modelo de los datos de login que se enviaran al backend para validar las credenciales.

ApiResponse:

data class ApiResponse (var isDone: Boolean = false, var jsonResponse : JsonElement? = null,
                   var msg: String? = null, var error: Boolean = false)

Login:

data class Login(var user: String, var password: String)

Modelo:

Ahora crearemos una clase repositorio, que es la que se encargara de proporcionarnos datos, en este caso la que hara la llamada al backend para validar las credenciales proporcionadas por el usuario en la interfaz grafica.

LoginRepository:

class LoginRepository {

    val tag: String = LoginRepository::class.java.simpleName
    var apiResponse = ApiResponse()

    fun performLogin(context: Context, loginRepositoryResponse: MutableLiveData<ApiResponse>,
                     user: String, password:String) {

        try {
            Api.login(context,
                Login(user, password),
                object : Api.RequestListener<JsonElement?> {

                    override fun onSuccess(response: JsonElement?) {
                        Utils.log(tag, response.toString())
                        Utils.saveLoginObject(response?.asJsonObject!!)
                        apiResponse.jsonResponse = response
                        apiResponse.isDone = true
                        loginRepositoryResponse.value = apiResponse
                    }

                    override fun onResponse() {
                    }

                    override fun onError(error: String?) {
                        apiResponse.error = true
                        if (error == Errors.ERROR_ENTITY_EXISTS) {
                            apiResponse.msg = context.getString(R.string.error_bad_request_invalid_params)

                        }
                        if (error == Errors.ERROR_ENTITY_NOT_EXISTS) {
                            apiResponse.msg = context.getString(R.string.client_not_exists_error)
                        }
                        apiResponse.isDone = true
                        loginRepositoryResponse.value = apiResponse
                    }
                })
        } catch (e: Exception) {
            Utils.error(tag, e.message, e)
        }
    }
}

Vista Modelo:

La siguiente clase es el puente entre nuestro repositorio y nuestra interfaz grafica:

LoginViewModel:

class LoginViewModel: ViewModel() {

    private val repository : LoginRepository = LoginRepository()
    var loginResponse: MutableLiveData<ApiResponse> = MutableLiveData<ApiResponse>()

    fun performLogin(context: Context, user: String, password:String) {
        loginResponse.value = ApiResponse()
        repository.performLogin(context, loginResponse, user, password)
    }
}

Vista:

Por ultimo tenemos nuestra vista, que es la parte con la que interactua el usuario:

LoginActivity:

class LoginActivity : BaseActivity(), View.OnClickListener {

    override val tag = LoginActivity::class.java.simpleName
    override val layoutId = R.layout.activity_login
    private lateinit var loginViewModel: LoginViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        loginViewModel =
            ViewModelProvider(this).get(LoginViewModel::class.java)

        loginBtn.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        when (view?.id) {
            R.id.loginBtn -> onLogin()
        }
    }

    private fun checkUser(): Boolean {
        if (TextUtils.isEmpty(usernameET.getText().toString().trim())) {
            Utils.buildOkDialog(this, getString(R.string.username_blank_error))
            return false
        } else if (!Patterns.EMAIL_ADDRESS.matcher(
                usernameET.getText().toString().trim()).matches()
        ) {
            Utils.buildOkDialog(this, getString(R.string.username_not_email_error))
            return false
        }
        return true
    }

    private fun checkData(): Boolean {
        if (!checkUser()) return false
        if (passwordET.text.toString().trim() == "") {
            Utils.buildOkDialog(this, getString(R.string.password_blank_error))
            return false
        }
        if (passwordET.text.toString().trim().length < 8) {
            Utils.buildOkDialog(this, getString(R.string.password_length_error))
            return false
        }
        return true
    }

    private fun onLogin() {
        if (checkData()) {
            isLoading()
            loginViewModel.loginResponse.observe(this, {
                if(it.isDone) {
                    isDone()
                    loginViewModel.loginResponse.removeObservers(this)
                    if(!TextUtils.isEmpty(it.msg))
                        Utils.buildOkDialog(this, it.msg!!)
                    startActivity(Intent(this@LoginActivity, MainActivity::class.java))
                }
            })
            loginViewModel.performLogin(this,
                usernameET.text.toString().trim(),
            Utils.getSHA1(passwordET.text.toString()))
        }
    }

}

 

Como podrás observar en el código mostrado, la parte de la Vista(LoginActivity) esta consiente solamente de la parte de la VistaModelo(LoginViewModel), pero jamás interactúa con la parte de la lógica del negocio. A su vez, la parte de la VistaModelo(LoginViewModel) tiene consciencia de la parte del repositorio(LoginRepository) pero no de la Vista(LoginActivity).

Conclusion

MVVM es un patrón de diseño muy potente y fácil de implementar. Como pudiste observar en el ejemplo, al implementar MVVM todo está más encapsulado, permitiendo con esto que nuestra aplicación sea más escalable, más fácil de leer, de mantener, de probar y por tanto más segura.

Espero este post les haya ayudado.

Hasta la próxima.

Eduardo Rodriguez
Eduardo Rodriguez

We use cookies to give you the best experience. Cookie Policy