CodeGym /Blog Java /Random-ES /Patrones y Singleton en Java
Autor
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Patrones y Singleton en Java

Publicado en el grupo Random-ES
Este artículo está dirigido a cualquier persona que, por primera vez, se encuentre con el concepto de patrones de diseño, haya escuchado el término singleton o haya implementado de alguna manera el patrón singleton pero no entendió lo que estaba sucediendo. ¡Bienvenido! Los estudiantes de CodeGym encuentran patrones de diseño por primera vez en el nivel 15, cuando el capitán les pide inesperadamente que "refuercen" su comprensión implementando el patrón Java Singleton con una implementación perezosa. Los estudiantes que escuchan sobre el patrón singleton por primera vez tienen muchas preguntas al instante: ¿qué demonios es un patrón de diseño? ¿Por qué lo necesitamos? ¿ Qué es un singletón ? Y finalmente, ¿qué es la implementación perezosa? Respondamos estas preguntas en orden.

¿Qué demonios es un patrón de diseño?

Creo que un poco de historia es para responder a esta pregunta con la mejor comprensión. Hay cuatro autores de programación famosos (Erich Gamma, John Vlissides, Ralph Johnson y Richard Helm) a quienes se les ocurrió una idea interesante. Se dieron cuenta de que el desarrollo de software a menudo requería que resolvieran aproximadamente los mismos problemas y escribieran código estructurado de la misma manera. Entonces decidieron describir patrones típicos que a menudo necesitan usarse en la programación orientada a objetos. Su libro se publicó en 1994 con el título Design Patterns: Elements of Reusable Object-Oriented Software. El nombre del libro resultó ser demasiado largo y la gente comenzó a llamarlo simplemente el libro de la Banda de los Cuatro. La primera edición incluía 23 patrones. Posteriormente, se descubrieron docenas de otros patrones.
Un patrón de diseño es una solución estandarizada a un problema común.
Y el patrón singleton es solo uno de ellos.

¿Por qué necesitamos patrones de diseño?

Puedes programar sin conocer patrones: después de todo, para el Nivel 15, ya has escrito cientos de miniprogramas en CodeGym sin siquiera saber que existen. Esto sugiere que los patrones de diseño son un tipo de herramienta cuyo uso distingue al maestro del aficionado: los patrones de diseño describen cómo resolver adecuadamente un problema típico. Esto significa que conocer los patrones le ahorra tiempo. De esa manera, son similares a los algoritmos. Por ejemplo, podría crear su propio algoritmo de clasificación con blackjack y números.y pasar mucho tiempo haciéndolo, o podría implementar uno que se haya entendido y descrito durante mucho tiempo. Lo mismo ocurre con los patrones de diseño. Además, con los patrones de diseño, el código se vuelve más estándar y, cuando se utiliza el patrón adecuado, es menos probable que se cometan errores, ya que los errores comunes del patrón se identificaron y eliminaron hace mucho tiempo. Además de todo lo demás, el conocimiento de los patrones ayuda a los programadores a entenderse mejor. Simplemente puede decir el nombre de un patrón en lugar de intentar proporcionar una explicación larga a sus compañeros programadores. En resumen, los patrones de diseño te ayudan a:
  • no reinventar la rueda, sino utilizar soluciones estándar;
  • estandarizar código;
  • estandarizar la terminología;
Para concluir este apartado, señalamos que todo el cuerpo de patrones de diseño se puede dividir en tres grandes grupos: Patrones y singleton - para todos los que los encuentran por primera vez - 2

Finalmente, el patrón singleton

Singleton es un patrón creacional . Este patrón garantiza que solo haya una instancia de una clase y proporciona un punto de acceso global para este objeto. De la descripción, debe quedar claro que este patrón debe aplicarse en dos casos:
  1. cuando su programa requiere que no se cree más de un objeto de una clase en particular. Por ejemplo, un juego de computadora puede tener una clase de héroe y solo un objeto de héroe que describa al único héroe del juego.

  2. cuando necesite proporcionar un punto para el acceso global a un objeto. En otras palabras, debe hacer que el objeto esté disponible desde cualquier parte del programa. Por desgracia, no es suficiente simplemente crear una variable global, ya que no está protegida contra escritura: cualquiera puede cambiar el valor de la variable, por lo que el punto de acceso global del objeto podría perderse. Estas propiedades de un Singleton son necesarias, por ejemplo, cuando tiene un objeto que funciona con una base de datos y necesita acceder a la base de datos desde diferentes partes del programa. Un Singleton se asegurará de que nadie escriba código que reemplace la instancia creada anteriormente.
Entonces, un Singleton satisface estas dos necesidades: debe haber solo uno de cierto tipo de objeto en el programa y debe haber acceso global a él. En el ejemplo del Nivel 15, el capitán le pide que implemente este patrón para la siguiente tarea:
  1. Encuentre un ejemplo de Singleton con inicialización diferida.

  2. Cree tres clases únicas (Sol, Luna, Tierra) en archivos separados usando el mismo principio.

  3. ImplementarPlanetainterfaz en las clases Sol , Luna y Tierra .

  4. En el bloque estático de la clase Solution llame alreadKeyFromConsoleAndInitPlanetmétodo.

  5. Implementar elreadKeyFromConsoleAndInitPlanetfuncionalidad del método:

    • 5.1. Lea un parámetro de cadena desde la consola

    • 5.2. Si el parámetro es igual a uno de losPlanetaconstantes de la interfaz, cree el objeto thePlanet adecuado .

Después de leer detenidamente las condiciones de la tarea, podemos ver claramente por qué se necesita un Singleton aquí. De hecho, se nos pide que creemos una instancia de cada una de las siguientes clases: Sol , Luna , Tierra . Tiene sentido suponer que no deberíamos crear más de un Sol/Luna/Tierra. De lo contrario, nos metemos en una situación absurda, a menos, por supuesto, que estés escribiendo tu versión de Star Wars. Implementando el patrón Singleton en Java en tres pasos En Java, el comportamiento Singleton no se puede implementar usando un constructor ordinario, porque un constructor siempre devuelve un nuevo objeto. Por lo tanto, todas las implementaciones de Singletonse reduce a ocultar el constructor, creando un método estático público que controla la vida útil del objeto único y "destruyendo" todos los objetos que aparecen recientemente. Si se accede a un Singleton , debe crear un nuevo objeto (si aún no existe uno en el programa) o devolver uno existente. Para lograr esto:
  1. Debe darle a la clase un campo estático privado que almacene un solo objeto:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance; // #1
    }
    
  2. Haga que el constructor (predeterminado) sea privado. Esto significa que no se puede acceder fuera de la clase y no podrá devolver nuevos objetos:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){} // #2
    } 
    
  3. Declare un método de creación estático que se usará para obtener el singleton:

    
    public class LazyInitializedSingleton {
        private static LazyInitializedSingleton instance;
            private LazyInitializedSingleton() {}
            public static LazyInitializedSingleton getInstance() { // #3
            if (instance == null) { // If the object has not yet been created
                instance = new LazyInitializedSingleton(); // Create a new object
            }
            return instance; // Return the previously created object
        }
    }
    
El ejemplo anterior es algo torpe, ya que simplemente ocultamos el constructor y proporcionamos nuestro propio método en lugar de un constructor estándar. Dado que este artículo tiene como objetivo garantizar que los estudiantes de CodeGym entren en contacto con este patrón (y los patrones de diseño en general), los matices de las implementaciones singleton más complejas no se describirán aquí. Solo notamos que, dependiendo de la complejidad del programa, es posible que este patrón deba refinarse aún más. Por ejemplo, en un entorno de subprocesos múltiples (consulte los artículos sobre subprocesos), varios subprocesos diferentes pueden acceder al método singleton simultáneamente y el código descrito anteriormente dejará de funcionar, ya que cada subproceso por separado podría crear una instancia de la clase. Como resultado, todavía hay varios enfoques diferentes para crear singletons seguros para subprocesos adecuados. Pero esa es otra historia =)

Y finalmente... ¿Qué es esta inicialización perezosa sobre la que preguntó el capitán?

La inicialización diferida también se denomina inicialización diferida. Este es un truco de programación en el que una operación de uso intensivo de recursos (y la creación de un objeto es una operación de uso intensivo de recursos) se realiza a pedido en lugar de por adelantado. Entonces, ¿qué sucede realmente en nuestro código Singleton Java? En otras palabras, nuestro objeto se crea en el momento en que se accede, no por adelantado. No debe asumir que la inicialización diferida está ligada de alguna manera rígida al patrón Singleton . La inicialización diferida también se usa en otros patrones de diseño creativo, como Proxy y Factory Method, pero esta también es otra historia =)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION