Crear un Singleton en Kotlin

David Mbochi Njonge 20 junio 2023
  1. Crear un Singleton usando la API de Kotlin
  2. Crear un Singleton utilizando un objeto complementario
  3. Crear un Singleton seguro para subprocesos mediante un objeto complementario
  4. Crear un Singleton utilizando el inicializador perezoso
  5. Conclusión
Crear un Singleton en Kotlin

Los patrones de diseño son un conjunto de soluciones a problemas comunes encontrados en función del requisito en cuestión, y estas soluciones se pueden reutilizar en otros sistemas que intentan resolver el mismo problema. Existen diferentes patrones de diseño, incluidos patrones de diseño creacional, conductual y estructural.

El patrón de diseño creacional se ocupa de cómo se crean los objetos, y en este tutorial aprenderá a usar el patrón singleton, que es un ejemplo del patrón de diseño creacional.

El patrón singleton es un patrón de diseño que ayuda a crear una copia única de un objeto y proporciona un punto de acceso a él.

Al desarrollar aplicaciones, hay situaciones en las que se requiere tener una sola copia de un objeto, como objetos para grupos de subprocesos, registro, controladores de dispositivos, configuraciones de registro, cachés y otros.

Un programa con varias copias de estos objetos puede provocar un comportamiento incorrecto, un uso excesivo de los recursos y resultados incoherentes. En este tutorial, aprenderemos diferentes formas que podemos usar para crear un singleton en Kotlin.

Crear un Singleton usando la API de Kotlin

Kotlin proporciona un singleton que podemos usar de inmediato sin escribir ningún código de creación de objetos para él. Para lograr esto, debemos definir nuestro nombre de singleton usando la palabra clave objeto, y dentro de este singleton, podemos agregar cualquier variable miembro y función.

Abra IntelliJ y seleccione Archivo> Nuevo> Proyecto. En la ventana que se abre, ingrese el nombre del proyecto como kotlin-singleton, seleccione Kotlin en la sección Idioma e Intellij en la sección Sistema de compilación.

Finalmente, presione el botón Crear para generar el proyecto.

Cree un archivo llamado Main.kt en la carpeta src/main/kotlin y copie y pegue el siguiente código en el archivo.

object Singleton{
    fun showMessage(): Singleton {
        return Singleton
    }
}

fun main(){
    println(Singleton.showMessage())
    println(Singleton.showMessage())
}

Como se mencionó anteriormente, esta es la forma predeterminada de crear un singleton en Kotlin y es muy fácil de implementar. Tenga en cuenta que el objeto singleton creado es seguro para subprocesos, lo que significa que incluso los programas multiproceso no pueden crear un nuevo objeto que podría generar inconsistencias en los datos.

En este código, hemos creado un singleton con el nombre Singleton y una función miembro llamada showMessage(). La función devuelve la referencia al singleton que se crea.

Ejecute este código y observe que las dos llamadas a la función miembro devuelven una referencia al mismo objeto, como se muestra a continuación.

Singleton@5305068a
Singleton@5305068a

Crear un Singleton utilizando un objeto complementario

Comente el ejemplo anterior y copie y pegue el siguiente código en el archivo Main.kt después del comentario.

class Singleton private constructor(){
    companion object{
        var singleton = Singleton();
        fun getInstance(): Singleton{
            if (singleton == null){
                singleton = Singleton();
            }
            return singleton;
        }
    }
}


fun main(){
    println(Singleton.getInstance());
    println(Singleton.getInstance());
}

La única diferencia entre el ejemplo anterior y este es que el objeto compañero se usa dentro de una clase. Todas las variables y funciones estáticas en Kotlin se colocan dentro del objeto complementario.

En este código, hemos creado un método llamado getInstance() que garantiza que solo se crea una copia del objeto. Para asegurarnos de que esto se cumple, hacemos que el constructor() sea privado y nos aseguramos de verificar si existe un objeto antes de crear uno.

Tenga en cuenta que este enfoque funciona bien, pero no es seguro para subprocesos. Si una aplicación es multiproceso, puede acceder al método getInstance() simultáneamente, lo que genera múltiples objetos que pueden generar los problemas mencionados anteriormente.

La siguiente sección muestra cómo hacer que el subproceso singleton sea seguro.

Ejecute este código y asegúrese de que la salida devuelva una referencia al mismo objeto que se muestra a continuación.

Singleton@1f32e575
Singleton@1f32e575

Crear un Singleton seguro para subprocesos mediante un objeto complementario

Comente el código anterior y copie y pegue el siguiente código en el archivo Main.kt después del comentario.

class Singleton private constructor(){
    companion object{

        private var singleton = Singleton();

        @Synchronized
        fun getInstance(): Singleton{
            if (singleton == null){
                singleton = Singleton();
            }
            return singleton
        }
    }
}

Tenga en cuenta que este código es similar al código anterior; la única diferencia es que agregamos la anotación @Synchronized en el método getInstance().

Este enfoque se denomina método sincronizado en Java. La seguridad del hilo se realiza a través de cerraduras.

Los objetos suelen tener bloqueos, y cuando un subproceso llega al método sincronizado, adquiere este bloqueo exclusivamente hasta que haya terminado de crear un objeto, lo que significa que ningún otro subproceso puede acceder a este método.

Cuando otro subproceso accede al bloqueo, ya tenemos un objeto y el subproceso utiliza el subproceso existente sin crear uno nuevo. Hay otras formas de lograr la sincronización, como usar el bloque sincronizado, que nos ayuda a sincronizar una sección del código en lugar de todo el método.

Tenga en cuenta que la sincronización resuelve nuestros problemas pero presenta problemas de rendimiento en nuestra aplicación. La sincronización hace que el rendimiento de nuestra aplicación se reduzca en un factor de 100, y deberíamos reconsiderar su uso si nuestra aplicación es crítica para el rendimiento.

Podemos usar enfoques para pasar de la inicialización perezosa a la inicialización entusiasta, el bloqueo verificado dos veces o dejar que permanezca si el rendimiento no afectará a la aplicación.

Crear un Singleton utilizando el inicializador perezoso

Comente el código anterior y copie y pegue el siguiente código en el archivo Main.kt después del comentario.

class Singleton{
    companion object{
        val singleton: Singleton by lazy {
            Singleton();
        }
    }

    fun showMessage(): Singleton {
        return Singleton.singleton
    }
}

fun main(){
    println(Singleton.singleton.showMessage())
    println(Singleton.singleton.showMessage())
}

En este código, hemos creado un singleton usando la función lazy() que crea una instancia de Lazy que es segura para subprocesos usando el argumento que le pasamos. Utiliza el LazyThreadSafetyMode.SYNCHRONIZED para garantizar que solo se use un bloqueo para inicializar la instancia diferida.

Ejecute este código y asegúrese de que la salida devuelva una referencia al mismo objeto que se muestra a continuación.

Singleton@3e3abc88
Singleton@3e3abc88

Conclusión

En este tutorial, hemos aprendido las diferentes formas que podemos usar para crear un objeto singleton en Kotlin. Los enfoques cubiertos incluyen el uso de la palabra clave objeto, el objeto complementario y la función inicializador perezoso.

Usando el método sincronizado, también hemos aprendido cómo garantizar que nuestro objeto singleton sea seguro para subprocesos cuando se usa un objeto complementario.

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub