Comentarios en: Definir una Singleton en Java mediante un enum http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/ Wed, 20 Jul 2016 11:45:40 +0000 hourly 1 http://wordpress.org/?v=3.4 Por: binary options trading http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-48103 binary options trading Sat, 04 Jun 2016 18:15:35 +0000 http://anabuigues.com/?p=348#comment-48103 <strong>binary options trading...</strong> Definir una Singleton en Java mediante un enum | El Blog de Ana Buigues... binary options trading…

Definir una Singleton en Java mediante un enum | El Blog de Ana Buigues…

]]>
Por: Riseven http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-92 Riseven Sun, 21 Feb 2010 23:54:41 +0000 http://anabuigues.com/?p=348#comment-92 Ah ok ok, mi culpa entonces, al principio no entendi bien a lo que te referias. Aunque se puedan instanciar nuevos objetos, el field INSTANCE va a seguir apuntando a la misma instancia, Gracias por explicar mejor a que te referias :) De todos modos ahora me pica la curiosidad de investigar si hay algún modo de romper eso... Ah ok ok, mi culpa entonces, al principio no entendi bien a lo que te referias. Aunque se puedan instanciar nuevos objetos, el field INSTANCE va a seguir apuntando a la misma instancia,

Gracias por explicar mejor a que te referias :)

De todos modos ahora me pica la curiosidad de investigar si hay algún modo de romper eso…

]]>
Por: Ana Buigues http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-91 Ana Buigues Sun, 21 Feb 2010 13:50:40 +0000 http://anabuigues.com/?p=348#comment-91 Hola Andrés, he reeditado el post, explicando lo de la protección contra la reflexión. Supongo que ahora sí que queda más claro. Saludos! Hola Andrés, he reeditado el post, explicando lo de la protección contra la reflexión. Supongo que ahora sí que queda más claro.

Saludos!

]]>
Por: Riseven http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-90 Riseven Wed, 17 Feb 2010 19:54:43 +0000 http://anabuigues.com/?p=348#comment-90 Pues entonces creo que no he entendido algo. A que te refieres con protección frente a reflexión? El código al final de mi anterior comentario consigue crear una nueva instancia del singleton por medio de reflexión. En cuanto a que se pueda hacer lo mismo en los casos anteriores, no se me ocurre como se podría hacer lo mismo con un enum otra que convirtiendo el antiguo singleton en un proxy. Pero eso significaria modificar todos los metodos de dicho singleton, que pueden ser unos cuantos. Si hay alguna forma mas sencilla me gustaría saberlo, pero si te refieres a esa, para mi es suficiente como para preferir poner por en medio un getInstance :) saludos! Andres Pues entonces creo que no he entendido algo. A que te refieres con protección frente a reflexión? El código al final de mi anterior comentario consigue crear una nueva instancia del singleton por medio de reflexión.

En cuanto a que se pueda hacer lo mismo en los casos anteriores, no se me ocurre como se podría hacer lo mismo con un enum otra que convirtiendo el antiguo singleton en un proxy. Pero eso significaria modificar todos los metodos de dicho singleton, que pueden ser unos cuantos. Si hay alguna forma mas sencilla me gustaría saberlo, pero si te refieres a esa, para mi es suficiente como para preferir poner por en medio un getInstance :)

saludos!
Andres

]]>
Por: Ana Buigues http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-89 Ana Buigues Wed, 17 Feb 2010 18:08:41 +0000 http://anabuigues.com/?p=348#comment-89 Hola Andrés, Desde mi punto de vista creo que puedes hacer exactamente lo mismo con el enum y con el getInstace en los casos que expones. Las ventajas que aporta el enum son la serialización de "serie" y el evitar los ataques de reflexión que se puedan hacer desde el exterior. En cuanto al resto, es todo lo mismo. Hola Andrés,

Desde mi punto de vista creo que puedes hacer exactamente lo mismo con el enum y con el getInstace en los casos que expones. Las ventajas que aporta el enum son la serialización de “serie” y el evitar los ataques de reflexión que se puedan hacer desde el exterior. En cuanto al resto, es todo lo mismo.

]]>
Por: Riseven http://anabuigues.com/2010/02/15/definir-una-singleton-en-java-mediante-un-enum/#comment-87 Riseven Wed, 17 Feb 2010 00:03:18 +0000 http://anabuigues.com/?p=348#comment-87 Hola, En primer lugar me ha llamado la atención esta forma de implementar el patron Singleton. Sin embargo yo sigo pensando que la mejor forma de implementarlo es tras un método getInstance. En primer lugar, comentas que el utilizar un Enum añade la protección contra doble instanciación por medio de Reflexión. Dudo que esto sea una ventaja, puesto que el poder crear nuevas instancias puede ser útil en casos excepcionales (frameworks, o unit tests), y al fin y al cabo la reflexión debería dejarse para casos excepcionales. Hace no mucho tiempo ayude a un compañero de trabajo en la tarea de hacer un unit test que probase que un determinado Singleton era thread-safe. El test creaba varios hilos que intentaban acceder a un singleton sin inicializar para ver si se devolvia mas de una instancia. Esto se repetia cien veces para intentar maximizar la probabilidad de que el test detectase un Singleton defectuoso. Entre cada uno de los intentos la instancia del singleton era puesta a null, y esto se hacía por reflexión. Según dices este unit test habría sido imposible de hacer con un enum. En definitiva, permitir que la reflexión se salte las barreras impuestas no lo veo como algo negativo, simplemente, la reflexion de java da mucho poder, y hay que tener cuidado con como se utiliza. Como nota al margen, si ahora tuviese que hacer esto, no utilizaría reflexión, sino que jugaría con ClassLoaders. Además, una desventaja seria que le veo a este metodo es en cuanto a encapsulación. Utilizando un enum estas declarando claramente que la clase Singleton es un singleton, mientras que lo bueno de utilizar un método getInstance() es que los detalles de implementación quedan ocultos. Cierto, utilizar un enum es como dices más conciso en cuanto a que se trata de un singleton, pero hasta que punto esto es positivo? Los sistemas de software evolucionan -generalmente a un ritmo mayor del que nos gustaría- y obligar a que todo tu codigo asuma que Singleton es un singleton puede ser un infierno de refactorización si un día te das cuenta de que tiene que dejar de serlo. Por poner un ejemplo, imaginemos que construimos un singleton DB que mediante DB.getInstance() nos da acceso a la base de datos. El tiempo pasa, el sistema empieza a crecer, y nos damos cuenta de que la base de datos esta saturada por lo que decidimos escalarla horizontalmente. Bien, solo tenemos que modificar DB.getInstance() para que maneje un pool de instancias, cada una apuntando a una base de datos distinta, y ya tienes todo tu sistema utilizando n bases de datos. Si hubieses implementado el singleton como un enum ahora tendrías todo tu código lleno de líneas DB.INSTANCE.*. La solución sería o una costosa refactorización para abandonar el enum, o bien incluir un complejo código de gestión de instancias basado en el current thread en cada uno de los métodos de DB. En definitiva, has sacrificado flexibilidad. Pero sigamos un poco más. Hasta ahora hemos visto como se ha sacrificado en flexibilidad para evitar que sea posible instanciar nuevas instancias (garantizar el patron singleton). Pero esto no es tan facil. En realidad aún hay situaciones en las que tu aplicación va a acabar teniendo más de un singleton. Por ejemplo, si tu aplicación tiene más de un ClassLoader (cada jar tiene su propio class loader, y los servidores de aplicaciones suelen hacer un uso importante de class loaders para "separar cosas"), es posible que más de un ClassLoader cargue tu enum Singleton, y con cada uno, su field INSTANCE, por lo que tendrás más de una instancia. De nuevo, si hubieses implementado el singleton con un getInstance(), tendrías el comodín de intentar alguna solución en este método, pero al usar un enum has perdido esa oportunidad. Por ejemplo, si en un momento dado te das cuenta de que tu aplicación esta cargando la clase DB con más de un ClassLoader y eso te está produciendo problemas, puedes simplemente escribir en el método getInstance codigo que comprueba si el ClassLoader que ha cargado esta clase DB es el SystemClassLoader (el ClassLoader raiz), si es así devuelve la instancia. Pero si no fuera así, lo que haría seria de proxy, y simplemente accedería a la clase DB a traves del SystemClassLoader y llamaría a su método getInstance. En la línea anterior, imaginemos que tenemos nuestra clase DB que implementa el patron singleton, pero de nuevo, los sistemas software evolucionan, y hemos decidido escalar la aplicación, que a partir de ahora se va a ejecutar en un cluster. Para ello se ha implementado un contexto de aplicación que permite la comunicación entre objetos en VM distintas. Hasta aquí todo bien, pero nos damos cuenta de que objetos en VM distintas, de nuevo, estan accediendo a instancias distintas del singleton. Puesto que cada objeto está en una VM distinta, tiene un SystemClassLoader distinto, no nos vale la solución anterior. Si realmente necesitásemos un singleton a nivel de aplicación, tendría que ser con ayuda del contexto de aplicación que hemos implementado. Y aqui es donde de nuevo vuelve a mostrarse la ventaja de haber programado el singleton con un getInstance(), puesto que si en ese método metemos el código necesario para interrogar al contexto de aplicación acerca del singleton, tendremos automáticamente todo el código funcionando de nuevo. Generalmente con singletons hay otro caso en el que se puede acabar creando más de una instancia y tiene que ver con la serialización. Si deserializas un objeto dos veces (con un reset por enmedio) acabas teniendo dos instancias distintas. Sin embargo, no se como funciona exactamente la deserialización de enums, de modo que desconozco cuanta seguridad ofrece un Enum frente al problema de la doble deserializacion. Resumiendo un poco, yo creo que este método, aunque sea más conciso, eso lo hace menos flexible. Que no permita la instanciación por reflexión en más bien una desventaja que una ventaja. Y encima al ganar protección contra la reflexión se está perdiendo capacidad de reacción frente a otros problemas más comunes como son multiples class loaders o multiples maquinas. Pero ahora viene lo mejor, resulta que los enums <strong>sí</strong> se pueden instanciar por reflexión: <code> Constructor ctor = Singleton.class.getDeclaredConstructor(String.class, int.class); Method acqMethod = ctor.getClass().getDeclaredMethod("acquireConstructorAccessor"); acqMethod.setAccessible(true); acqMethod.invoke(ctor); Field accessorField = ctor.getClass().getDeclaredField("constructorAccessor"); accessorField.setAccessible(true); Object accessor = accessorField.get(ctor); Method newInstanceMethod = accessor.getClass().getMethod("newInstance", Object[].class); newInstanceMethod.setAccessible(true); Singleton otroSingleton = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}}); </code> En cualquier caso me ha gustado mucho leer tu post, puesto que me ha hecho aprender algo nuevo y eso siempre es bueno :) Saludos Andrés Hola,

En primer lugar me ha llamado la atención esta forma de implementar el patron Singleton. Sin embargo yo sigo pensando que la mejor forma de implementarlo es tras un método getInstance.

En primer lugar, comentas que el utilizar un Enum añade la protección contra doble instanciación por medio de Reflexión. Dudo que esto sea una ventaja, puesto que el poder crear nuevas instancias puede ser útil en casos excepcionales (frameworks, o unit tests), y al fin y al cabo la reflexión debería dejarse para casos excepcionales. Hace no mucho tiempo ayude a un compañero de trabajo en la tarea de hacer un unit test que probase que un determinado Singleton era thread-safe. El test creaba varios hilos que intentaban acceder a un singleton sin inicializar para ver si se devolvia mas de una instancia. Esto se repetia cien veces para intentar maximizar la probabilidad de que el test detectase un Singleton defectuoso. Entre cada uno de los intentos la instancia del singleton era puesta a null, y esto se hacía por reflexión. Según dices este unit test habría sido imposible de hacer con un enum. En definitiva, permitir que la reflexión se salte las barreras impuestas no lo veo como algo negativo, simplemente, la reflexion de java da mucho poder, y hay que tener cuidado con como se utiliza. Como nota al margen, si ahora tuviese que hacer esto, no utilizaría reflexión, sino que jugaría con ClassLoaders.

Además, una desventaja seria que le veo a este metodo es en cuanto a encapsulación. Utilizando un enum estas declarando claramente que la clase Singleton es un singleton, mientras que lo bueno de utilizar un método getInstance() es que los detalles de implementación quedan ocultos. Cierto, utilizar un enum es como dices más conciso en cuanto a que se trata de un singleton, pero hasta que punto esto es positivo? Los sistemas de software evolucionan -generalmente a un ritmo mayor del que nos gustaría- y obligar a que todo tu codigo asuma que Singleton es un singleton puede ser un infierno de refactorización si un día te das cuenta de que tiene que dejar de serlo.
Por poner un ejemplo, imaginemos que construimos un singleton DB que mediante DB.getInstance() nos da acceso a la base de datos. El tiempo pasa, el sistema empieza a crecer, y nos damos cuenta de que la base de datos esta saturada por lo que decidimos escalarla horizontalmente. Bien, solo tenemos que modificar DB.getInstance() para que maneje un pool de instancias, cada una apuntando a una base de datos distinta, y ya tienes todo tu sistema utilizando n bases de datos. Si hubieses implementado el singleton como un enum ahora tendrías todo tu código lleno de líneas DB.INSTANCE.*. La solución sería o una costosa refactorización para abandonar el enum, o bien incluir un complejo código de gestión de instancias basado en el current thread en cada uno de los métodos de DB. En definitiva, has sacrificado flexibilidad.

Pero sigamos un poco más. Hasta ahora hemos visto como se ha sacrificado en flexibilidad para evitar que sea posible instanciar nuevas instancias (garantizar el patron singleton). Pero esto no es tan facil. En realidad aún hay situaciones en las que tu aplicación va a acabar teniendo más de un singleton. Por ejemplo, si tu aplicación tiene más de un ClassLoader (cada jar tiene su propio class loader, y los servidores de aplicaciones suelen hacer un uso importante de class loaders para “separar cosas”), es posible que más de un ClassLoader cargue tu enum Singleton, y con cada uno, su field INSTANCE, por lo que tendrás más de una instancia. De nuevo, si hubieses implementado el singleton con un getInstance(), tendrías el comodín de intentar alguna solución en este método, pero al usar un enum has perdido esa oportunidad.
Por ejemplo, si en un momento dado te das cuenta de que tu aplicación esta cargando la clase DB con más de un ClassLoader y eso te está produciendo problemas, puedes simplemente escribir en el método getInstance codigo que comprueba si el ClassLoader que ha cargado esta clase DB es el SystemClassLoader (el ClassLoader raiz), si es así devuelve la instancia. Pero si no fuera así, lo que haría seria de proxy, y simplemente accedería a la clase DB a traves del SystemClassLoader y llamaría a su método getInstance.

En la línea anterior, imaginemos que tenemos nuestra clase DB que implementa el patron singleton, pero de nuevo, los sistemas software evolucionan, y hemos decidido escalar la aplicación, que a partir de ahora se va a ejecutar en un cluster. Para ello se ha implementado un contexto de aplicación que permite la comunicación entre objetos en VM distintas. Hasta aquí todo bien, pero nos damos cuenta de que objetos en VM distintas, de nuevo, estan accediendo a instancias distintas del singleton. Puesto que cada objeto está en una VM distinta, tiene un SystemClassLoader distinto, no nos vale la solución anterior. Si realmente necesitásemos un singleton a nivel de aplicación, tendría que ser con ayuda del contexto de aplicación que hemos implementado. Y aqui es donde de nuevo vuelve a mostrarse la ventaja de haber programado el singleton con un getInstance(), puesto que si en ese método metemos el código necesario para interrogar al contexto de aplicación acerca del singleton, tendremos automáticamente todo el código funcionando de nuevo.

Generalmente con singletons hay otro caso en el que se puede acabar creando más de una instancia y tiene que ver con la serialización. Si deserializas un objeto dos veces (con un reset por enmedio) acabas teniendo dos instancias distintas. Sin embargo, no se como funciona exactamente la deserialización de enums, de modo que desconozco cuanta seguridad ofrece un Enum frente al problema de la doble deserializacion.

Resumiendo un poco, yo creo que este método, aunque sea más conciso, eso lo hace menos flexible. Que no permita la instanciación por reflexión en más bien una desventaja que una ventaja. Y encima al ganar protección contra la reflexión se está perdiendo capacidad de reacción frente a otros problemas más comunes como son multiples class loaders o multiples maquinas.

Pero ahora viene lo mejor, resulta que los enums se pueden instanciar por reflexión:


Constructor ctor = Singleton.class.getDeclaredConstructor(String.class, int.class);
Method acqMethod = ctor.getClass().getDeclaredMethod("acquireConstructorAccessor");
acqMethod.setAccessible(true);
acqMethod.invoke(ctor);
Field accessorField = ctor.getClass().getDeclaredField("constructorAccessor");
accessorField.setAccessible(true);
Object accessor = accessorField.get(ctor);
Method newInstanceMethod = accessor.getClass().getMethod("newInstance", Object[].class);
newInstanceMethod.setAccessible(true);
Singleton otroSingleton = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}});

En cualquier caso me ha gustado mucho leer tu post, puesto que me ha hecho aprender algo nuevo y eso siempre es bueno :)

Saludos
Andrés

]]>