Implementación del Patrón Singleton en Kotlin para Android

Clase 11 de 32Curso de Patrones de Diseño en Android

Resumen

¿Qué es el patrón de diseño Singleton?

El patrón de diseño Singleton es uno de los patrones de creación más utilizados en la programación. Su objetivo es garantizar que una clase tenga solo una instancia y proporciona un punto de acceso global a esa instancia. Es especialmente útil cuando un objeto singular necesita coordinar acciones a lo largo de un sistema o mantener un estado global compartido.

¿Cuándo usar el patrón Singleton?

El Singleton se emplea en situaciones donde:

  • Se requiere una única instancia de una clase a lo largo de toda la aplicación.
  • Mantener una instancia separada para cada flujo sería ineficiente y costoso en términos de procesamiento.
  • Se necesita compartir datos entre diferentes flujos o módulos de aplicación de manera centralizada.

Por ejemplo, en una aplicación donde se maneja la información de un usuario a lo largo de diferentes pantallas o procesos, se puede utilizar un Singleton para almacenar datos del usuario, evitando la repetición de instancias.

¿Cuáles son los beneficios del patrón Singleton?

Los beneficios más destacados del Singleton son:

  • Optimización: Reducir el uso de memoria al no crear múltiples instancias de la misma clase.
  • Consistencia: Ofrece un punto único de acceso a una instancia que mantiene su estado a lo largo del ciclo de vida de la aplicación.
  • Flexibilidad: Permite un acceso fácil desde cualquier parte del código a los datos significativos y utilizados frecuentemente.

¿Cómo implementar un Singleton en Kotlin?

El patrón Singleton en Kotlin se puede implementar de manera sencilla. A continuación, se explica paso a paso cómo crearlo.

Creación del Singleton

Para implementar un Singleton, primero se necesita establecer una clase que manejará la instancia deseada. Por ejemplo, para manejar instancias de usuario:

class UserSingleton private constructor() {
    // Datos del usuario
    var userName: String? = null

    companion object {
        @Volatile private var instance: UserSingleton? = null

        // Función estática para obtener la instancia
        fun getInstance(): UserSingleton =
            instance ?: synchronized(this) {
                instance ?: UserSingleton().also { instance = it }
            }
    }
}

Uso del Singleton en la aplicación

Una vez implementada la clase Singleton, se puede acceder a los datos de usuario fácilmente, como en el siguiente ejemplo:

fun main() {
    // Se obtiene la instancia del Singleton
    val userSingleton = UserSingleton.getInstance()

    // Establece un valor
    userSingleton.userName = "Christian"

    // Accede al mismo valor desde otra parte de la aplicación
    val anotherAccess = UserSingleton.getInstance()
    println(anotherAccess.userName)  // Output: Christian

    // Cambia el valor desde otro contexto
    anotherAccess.userName = "Hola"
    println(userSingleton.userName)  // Output: Hola
}

Al usar este patrón, cualquier llamada a UserSingleton.getInstance() retornará la misma instancia, con el mismo estado compartido.

Problemas comunes y soluciones

Es importante recordar que todos los métodos y propiedades que necesitan ser accesibles globalmente deben ser marcados como estáticos (usando companion object en Kotlin). Esto asegura que las funciones asociadas con el Singleton sean verdaderamente accesibles en toda la aplicación.

Por otro lado, Encapsular el manejo de la instancia dentro del companion object previene problemas de concurrencia y asegura que el Singleton se inicializa correctamente, controlando así el acceso simultáneo desde múltiples hilos.

Ejemplo práctico

Suponga que desea mantener una instancia de Singleton en un contexto de transferencia de saldo de usuario:

class BalanceSingleton private constructor() {
    var currentBalance: Double = 0.0

    companion object {
        private var instance: BalanceSingleton? = null

        fun getInstance(): BalanceSingleton {
            if (instance == null) {
                instance = BalanceSingleton()
            }
            return instance!!
        }
    }
}

Puede modificar currentBalance desde cualquier parte de la aplicación, manteniendo un estado consistente del saldo actual de un usuario.

La utilización del patrón Singleton no solo optimiza el rendimiento, sino que también estructura mejor el manejo de recursos en una aplicación. Si has considerado otras formas de integrar Singleton en tus proyectos, comparte tus ideas en la sección de comentarios para seguir aprendiendo juntos y mejorando nuestras prácticas de programación.