Implementación del Patrón Singleton en Kotlin para 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 privateconstructor(){// Datos del usuariovar userName: String?=nullcompanionobject{@Volatileprivatevar instance: UserSingleton?=null// Función estática para obtener la instanciafungetInstance(): 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:
funmain(){// Se obtiene la instancia del Singletonval userSingleton = UserSingleton.getInstance()// Establece un valor userSingleton.userName ="Christian"// Accede al mismo valor desde otra parte de la aplicaciónval 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:
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.
El profesor esta utilizando la misma implementación del SIngleton que se utiliza en Java.. pero en Kotlin es supremamente breve hacer un singleton y es de esta forma:
object UserSingleton{ val userName="cristian"}
Y para usarlo.
UserSingleton.userName
¿Muy breve verdad? Solo hay que declarar una clase con la palabra object en vez de class para convertirlo en un singleton.
Creo que el profesor le falta más experiencia en Kotlin.
Hola amigo, creo que el objetivo de la clase no es como hacer un Singleton en Kotlin, si no mostrar la manera en cómo se hace crea un singleton y como puedes ver esta manera funciona en Java y Kotlin lo cual es el objetivo de los patrones que funcione sin importar el lenguaje pese a las ayudas que cada lenguaje pueda darnos.
Esto lo explican en la siguiente clase. A mi me parece bien que expliquen las racies de donde nacen las cosas.
Le faltó hacer al constructor del UserSingleton private
classUserSingletonprivateconstructor()
Si no se hace Private entonces no tiene ningún sentido el resto de la implementación debido a que sin el podríamos crear tantas instancias distintas como quisieramos
Companion Object es muy parecido a @staticmethod en Python
Yo usuaria Singleton, para persistir datos que necesite en todo el proceso de la aplicación, datos que no sea mutables.
No entiendo para que sería necesario crear un singleton para solo el username. No sería mejor que la variable sea publica y estática y usarla como UserSingleton.username? Entiendo que probablemente no sea una buena práctica por el tema de encapsulamiento y eso, pero sirve y evitaría crear un singleton. Por qué es necesario crear un singleton allí? Lo veo más necesario para una instancia de retrofit por ejemplo, que es donde mayormente uso el singleton.
Hola Carlos, tienes toda la razón, en este caso lo hicimos netamente para explicar el patrón dentro del proyecto ya que si lo hacía con retrofit tendría que explicar como tal retrofit. Fue netamente por dar un ejemplo académico.
Lo usaria por ejemplo para el usuario logeado, lo usaria por ejemplo si tengo un reproductor de video, siempre retornar la misma instancia del player y solo jugar con funciones internas para reproducir diferentes videos. Lo usaria para instanciar un objeto que me ayude a acceder a la base de datos, no veo la utilidad de tener muchas instancias, con una es suficiente. Bastante util
Yo usaría Singleton para definir instancias de SQlite, por ejemplo lo utilizo en mis proyectos para usar Room y con eso solo construyo la base de datos una sola vez.
Muy bueno el curso hasta este momento. Gracias Cristian.
Estás haciendo un cast de un valor nulleable a uno non-nullable, eso es una súper mala práctica. Aún no me creo que este curso esté en la cima de toda la ruta de aprendizaje. :/
Singleton en Flutter:
classUserSingleton{static final UserSingleton _instance =UserSingleton._internal();String userName ='Alvaro';// using a factory is important// because it promises to return _an_ object of this type// but it doesn't promise to make a new one. factory UserSingleton(){return _instance;}// This named constructor is the "real" constructor// It'll be called exactly once, by the static property assignment above// it's also private, so it can only be called in this classUserSingleton._internal(){// initialization logic}// rest of class as normal, for example:voidupdateUserName(String name){ userName = name;}}
Pueden revisar mi articulo sobre singleton en Flutter aquí.
Creo que falto el punto de cuándo no usarlo.
Y esta claro que fue mas por temas académicos que haya creado la clase de username static, pero hay que recalcarlo en el curso, ya que usarlo de esta forma nos va a acoplar fuertemente el código, y al momento de querer testear se nos complicaría.
SIngleton lo puedo usar para las los datos de login
Si claro que si
¿Cuándo usarlo?
Cuando queremos datos
Datos que son útiles para múltiples flujos
Para tener acceso global a los datos
Beneficios:
Una única instancia de los datos transversales
Acceso desde cualquier lugar
Se crea la instancia solamente cuando se va a usar
Singleton es la única instancia para toda la aplicación, se lo puede usar cuando necesitemos una sola instancia, y este podrá ser para múltiples flujos.
Nota: los datos deben ser lo mas importante
Cristian debería saber que hoy perdió el Santa Fé
Profesor, no es más sencillo usar esto, puesto que object crea un singleton.
object Sesion{
var nombre = "Gustavo"
}
Llamado:
Sesion.nombre = "Jose"
Hola Gustavo, claro que sí así es mucho más fácil, pero de esa manera no entenderías nunca que pasa por detrás, por eso lo hice de la forma donde sabemos que pasa con las instancias, para hacer clara la explicación.
No deberia ser el constructor Privado?, si no es asi igual podrías tener tantas instancias como quieras
Sí, debería ser privado. Incluso el método getInstance() también debería ser synchronized en Java para evitar ser creado desde otros hilos en el sistema. Pero en Kotlin no es necesario debido a que tenemos el tipo de clase object.
Mayormente utilizo este patrón para utilidades promedio. Por ejemplo que necesite manejar cálculos entre fechas. Entonces creo una clase llamada DateManager, y cómo esta la uso común en diferentes fragmentos o actividades solo para un par de cosas, entonces la hago singleton para manejarlo todo desde ahí. RECOMENDACIÓN: No utilicen este patrón cuando requieran de almacenar el contexto de la aplicación, puede ocasionar problemas entre actividades y crashear tu App en escenarios muy específicos.
Como dice Mario, almacenar el contexto de las actividades o fragmentos en instancias Singleton ocasiona Fugas de Memoria (Memory Leaks) ya que el Garbage Collector no podrá eliminar las instancias de las actividades o fragmentos una vez que se ejecuta el método onDestroy() en el ciclo de vida de la actividad o fragmento ya que seguirá existiendo una referencia a esa instancia en el objeto singleton. Para evitar esto, es mejor utilizar el contexto de la aplicación y no el de las actividades o fragmentos
Me parece bueno para usar digamos en el nickname, que cuando lo cambies en un lugar, se cambie en todos (hablando de una aplicación que no traiga esa info desde el backend)
Yo utilizaria el Singleton para obtener variables de tipo global en todo el aplicativo.
Algo importante sobre los Singletons es que el constructor tiene que ser privado, de otra forma cualquier clase podría crear una nueva instancia