Patrones de diseño más utilizados en Android

En el desarrollo de software existen problemas generales que siempre nos encontraremos y sin importar el lenguaje estemos utilizando, nos toparemos con los mismos problemas, he aquí donde entran los patrones de diseño.

¿Qué son los patrones de diseño?

Los patrones de diseño son soluciones generales y re utilizables a problemas comunes en el desarrollo de software, estas plantillas nos pueden ayudar a resolver problemas comunes que surgen a la hora de desarrollar software de una manera fácil y sencilla y se pueden aplicar a cualquier lenguaje de programación que se esté utilizando.

Categorías de patrones de diseño

Existen 3 categorías principales de patrones de diseño, cada categoría tiene una función especifica que se explica continuación:

Patrones creacionales

Nos ayudan a delegar la responsabilidad de creación de objetos utilizando diversos métodos que aumentan la flexibilidad y re utilización de código.

Sus 2 pilares fundamentales son: Encapsular el conocimiento de las clases y ocultar como se crea e instancía la clase.

Más utilizadas en el desarrollo de Android

  • Singleton

  • Builder

  • Factory

Patrones estructurales

Sirven para definir cómo se componen los objetos para formar estructuras mayores y nuevas funcionalidades.

Lo más utilizado en Android

  • Adapter

  • Fecade

Patrones de comportamiento

Se utilizan para definir cómo se comunicarán e interactuarán los objetos entre ellos.

Lo más utilizado en android

  • Observer

  • Iterator

Su utilización en android

Estos patrones de diseño se utilizan mucho en el desarrollo de aplicaciones Android, la API de Android y muchas librerías hacen uso de éstos para facilitar el desarrollo y el uso de de las mismas, es por eso que es importante conocerlos y aplicarlos.

Todos hemos utilizado el componente de RecyclerView, éste es un buen ejemplo del uso de patrones de diseño ya que para poder mostrar los diferentes elementos en el RecyclerView se usa un Adaptador que es el patrón estructural adapter, otro ejemplo son los AlertDialog que con su clase builder se pueden crear diálogos de forma muy sencilla que es un ejemplo del patrón creacional Builder.

Ejemplos de patrones de diseño

Singleton (patrón creacional)

El patrón creacional singleton resuelve el problema de la creación excesiva de objetos idénticos de una misma clase, lo que hace este patrón es instanciar sólo una vez el objeto de la clase y devolver ese objeto cada que se requiera evitando así volverlo a instanciar, este patrón suele utilizarse cuando creamos muchos objetos idénticos de una clase en los diferentes módulos de la aplicación resolviendo el uso excesivo de memoria, en Android se utiliza mucho para instanciar objetos de base de datos, ya que crear objetos de este tipo gasta muchos recursos.

Ejemplo

class MySingleton private constructor(){

	companion object{

		private var singleton: MySingleton? = null

		@JvmStatic
		fun getInstance(): MySingleton{

			if(singleton == null){
				singleton = MySingleton()
			}

			return singleton!!

		}

	}

}

Donde tenemos un constructor privado que por ende el objeto solo se podrá instanciar dentro de la misma clase y una variable privada que se instanciará una sola vez y siempre se retornará ese objeto y, para acceder u obtener el objeto, será a través del método getInstance() de la siguiente manera:

val mySingleton = MySingleton.getInstance()

Así no importa donde necesitemos el objeto siempre nos retornará el mismo.

Factory (patrón creacional)

El patrón factory nos permite crear diferentes tipos objetos de diferentes clases a través de una interfaz, de una manera sencilla utilizando un objeto factory, este objeto tiene la responsabilidad de la creación de los objetos para poderlos obtener de una manera sencilla.

Ejemplo

interface ISnackbar {

	fun getSnackbar(): Snackbar

}

class SnackbarError(): ISnackbar {

	override fun getSnackbar(): Snackbar {
		// creación del objeto
	}

}

class SnackbarLoading(): ISnackbar {

	override fun getSnackbar(): Snackbar {
		// creación del objeto
	}

}

class SnackbarSuccess(): ISnackbar {

	override fun getSnackbar(): Snackbar {
		// creación del objeto
	}

}

object SnackbarFactory {

	const val TYPE_SUCCESS = 1
	const val TYPE_ERROR = 2
	const val TYPE_LOADING = 3

	fun getSnackbar(type: Int): Snackbar = when(type){
		TYPE_LOADING -> SnackbarLoading().getSnackbar()
		TYPE_ERROR -> SnackbarError().getSnackbar()
		else -> SnackbarSuccess().getSnackbar()
	}

}



public class Main {

	fun main() {
		val snackbar = SnackbarFactory.getSnackbar(SnackbarFactory.TYPE_LOADING)
		snackbar.show()
	}

}

Como podemos observar en el siguiente código, utilizando la clase SnackbarFactory es muy sencillo crear diferentes instancias de un Snackbar solo mandando el tipo de la Snackbar que se necesite.

Observer (Patrón de comportamiento)

El patrón de comportamiento observer se utiliza para notificar a diferentes objetos el cambio de un objeto al que están suscritos, esto quiere decir notificar a los objetos que dependen de un objeto el cambio del mismo.

Un ejemplo de este patrón es la librería LiveData de Android para mas información te sugiero visitar su pagina:

https://developer.android.com/topic/libraries/architecture/livedata.

Para utlizarlo se requiere 2 interfaces IObservable que se utilizará para los objetos que pueden ser observados y IObserver para los objetos que van a observar y a estos objetos se les notificara de un cambio en el objeto observado a través del metodo notify().

Ejemplo

interface IObserver{

	fun notify()

}

interface IObservable{

	fun addObserver(observer: IObserver)
	fun removeObserver(observer: IObserver)
	fun notifyAll()

}

class observable: IObservable{

	private val listObservers = ArrayList<IObserver>()

	override fun addObserver(observer: IObserver) {
		listObservers.add(observer)
	}

	override fun removeObserver(observer: IObserver) {
		listObservers.remove(observer)
	}

	override fun notifyAll(){
		for(observer in listObservers){
			observer.notify()
		}
	}

}

class observer: IObserver{

	override fun notify() {
		// Realizar algún cambio
	}

}

Este es un ejemplo del patrón Observer, los observadores se registran al objeto observado a través del método addObserver() y anulan su suscripción con el método removeObserver() y cuando el observado tenga algún cambio le notificará a sus observadores a través del método notifyAll() y los observadores actuarán a través del método notify().

Adapter (Patrón estructural)

Se utiliza cuando tenemos diferentes tipos de objetos incompatibles pero queremos que cooperen entre sí, el adapter se encarga de adaptar los datos de los diferentes objetos para que se puedan utilizar de forma sencilla.

Un ejemplo muy utilizado en Android es el RecyclerView que a través de un adaptador le decimos cómo se deben mostrar los datos.

Ejemplo:

interface IAdapter{

	fun aumentarGrados(gradosCelsius: Int)
	fun disminuirGrados(gradosCelsius: Int)
	fun getGrados(): Int

}

class GradosFahrenheit{

	var fahrenheit = 0.toDouble()

	fun agregarGrados(grados: Double){
		fahrenheit += grados
	}

	fun disminuirGrados(grados: Double){
		fahrenheit -= grados
	}

}

class GradosCelsiusFahrenheitAdapter(val gradosFahrenheit: GradosFahrenheit): IAdapter{

	override fun aumentarGrados(gradosCelsius: Int) {
    
                gradosFahrenheit.agregarGrados(transformarCelsiusFahrenheit(gradosCelsius))
	}

	override fun disminuirGrados(gradosCelsius: Int) {
		gradosFahrenheit.disminuirGrados(transformarCelsiusFahrenheit(gradosCelsius))
	}

	override fun getGrados(): Int {
		return ((gradosFahrenheit.fahrenheit - 32) - 1.8).toInt()
	}

	private fun transformarCelsiusFahrenheit(gradosCelsius: Int): Double = (gradosCelsius * 1.8) + 32

}

Con el patron adapter es muy sencillo hacer que dos objetos que son incompatibles puedan cooperar entre sí, como se puede observar en el ejemplo el uso de un adaptador hace más sencillo lograr este objetivo.

Conclusión

En conclusión, si haz utilizado librerías y la API de Android, seguramente alguno de estos patrones te resultó familiar. Recuerda que estos patrones son soluciones generales a problemas comunes en el desarrollo de software y son muy utilizados en Android ya que nos presentan soluciones concretas y sencillas a problemas comunes y nos pueden ayudar a ahorrar tiempo en el desarrollo, a estar seguro de la validez del código y establecer un lenguaje común entre desarrolladores, los patrones de diseño son muy sencillos de utilizar y nos ayudarán a desarrollar software de mayor calidad y facilitarnos el desarrollo del mismo, también nos ayudaran a que otros programadores les sea mas fácil entender el código del desarrollo ya que son ampliamente utilizados.