Consideraciones sobre el uso del Patrón Singleton desde un entorno web con PHP

Uno de los usos habituales del Patrón de Diseño Singleton es concentrar en un único objeto todas las llamadas a la base de datos y que este se encargue de que nuestro sistema use una única conexión, compartida por todos los que la necesiten.

La lógica del patrón es simple

La lógica es simple, la primera vez que alguna parte de nuestro sistema necesita conectarse a la base de datos, se le pide una instancia de conexión al Singleton implementado en nuestra clase de Persistencia (una clase que separa nuestro código de "lógica de negocio" del código explícito para trabajo con base de datos). Si en las siguientes peticiones esta instancia existe, el Singleton devuelve siempre la misma, así se reutiliza sin crear nuevas.

Ventajes del uso del patrón

Esto evita que nuestro sistema, en un momento dado, tenga innumerables y descontroladas conexiones a la base de datos, consumiendo recursos y tal vez, el máximo permitido por vez (configurado en la misma base de datos). A su vez, al tener todas las conexiones centralizadas, podemos implementar todo tipo de controles y auditorías (registrar cantidad de conexiones, que partes de nuestro sistema realiza más conexiones, horarios para las mismas, etc, etc).

El problema

Lo habitual en "sistemas de escritorio" (no web) es que esta instancia se mantenga generalmente durante toda la vida del sistema, lo cual sería acertado decir que la instancia reutilizada de conexión es siempre la misma (única). Pero en ambientes web, el contexto es distinto y la forma de trabajo cambia.

Por esta razón es muy común que se repita la siguiente consulta en foros:

"Una vez ejecutado el patrón en un página/fuente/scripts, los objetos se destruyen, desapareciendo la instancia del Singleton. Por lo que si se ejecutara nuevamente, todo volvería a repetirse de cero, y no se aprovecharía en toda la aplicación, solo por página."

El problema es ese, el mundo "stateless" ("estado desconectado") de la web, donde queda claramente evidenciado al intentar implementar el patrón "Singleton".

Cuando tu estás, por ejemplo, en una aplicación desktop hecha en Java, tu código está siempre corriendo, de forma permanente. Si tu creas una conexión con la base de datos, siempre estará ahí mientras viva la aplicación (o te desconecte el servidor de base de datos, etc), y cada vez que invoques el "singleton" este funcionará correctamente en todos los casos.

En situaciones "típicas" de la web (sin las conexiones persistentes), luego de terminar la ejecución de un página, pasamos al "estado desconectado" y perdemos todos los objetos. En este contexto, solo tendría sentido el "singleton" si tu página debe hacer llamadas a varios objetos que tienen acceso a la base de datos y tu quieres tener control sobre estos accesos.

Pero luego de concluida la página (terminamos de ejecutar la última línea de nuestro php) terminó la función del "singleton", y el próximo contexto será una nueva ejecución de la página, esta misma u otra distinta.

Conclusiones finales

Resumiendo, serían:

"Se puede implementar perfectamente el patrón Singleton con PHP, pero por el contexto web, este se aplicará normalmente durante la vida de una única página, a menos que implementemos algún mecanismo para persistir el objeto Singleton y así lograr que este perdure durante toda la vida de la aplicación web"

Estas son mis conclusiones aplicadas al mundo PHP desde el mundo Java; si no les gustan, tengo otras

Artículo basado en una respuesta que di en Foros del Web:

4 comentarios:

Anónimo dijo...

Un par de aclaraciones/consejos:

En el mundo java el patron singleton funciona en desktop y por supuesto también en web.

La instancia "singleton" se almacena en una propiedad estatica de la clase singleton, por tanto esta presente desde que la clase se carga en la VM hasta que se descarga, en web esto implica, desde que se arranca el servidor de aplicaciones hasta que se para.

De hecho los servlets son siempre objetos de tipo singleton, por tanto bastaría con almacenar la conexión a bd en una propiedad del servlet.

Otra cosilla, el ejemplo que pones ilustra bien el funcionamiento del patron pero en si mismo una practica poco o nada recomendable.

Las aplicaciones web reciben multiples peticiones de forma simultanea, tener una sóla conexión abierta con el SGBD es un cuello de botella enorme. Otro gravisimo problema de este enfoque son las transacciones, que se hacen a nivel de conexión con la bd.

La solución habitual para acceso a bd desde paginas web es usar un "pool de conexiones", defines un número maximo de conexiones abiertas y en cada petición le solicitas al pool una conexión libre. De esta manera controlas el número de conexiones maximo, no tienes cuellos de botella y permites el uso de transacciones.

En java (en .net también) esto se hace de forma estándar a través de los datasources de los servidores de aplicaciones, también existen multitud de librerías open-source que proporcionan pooling.

En PHP tienen que existir soluciones similares.

enrique_place dijo...

> Un par de aclaraciones/consejos:

Ok, siempre es bienvenido el intercambio; la discusión enriquece ;-)

> En el mundo java el patron
> singleton funciona en desktop y
> por supuesto también en web.

Estamos de acuerdo.

> La instancia "singleton" se
> almacena en una propiedad
> estatica de la clase singleton,
> por tanto esta presente desde que
> la clase se carga en la VM hasta
> que se descarga, en web esto
> implica, desde que se arranca el
> servidor de aplicaciones hasta
> que se para.

Ok, también estoy de acuerdo (aunque mis conocimientos sobre estos temas en Java son meramente teóricos y básicos).

> De hecho los servlets son siempre
> objetos de tipo singleton, por
> tanto bastaría con almacenar la
> conexión a bd en una propiedad
> del servlet.

Esto es nuevo para mi, no tenía tan claro el concepto de "singleton" en los servlets.

> Otra cosilla, el ejemplo que
> pones ilustra bien el
> funcionamiento del patron pero en
> si mismo una practica poco o
> nada recomendable.

Bueno, dos puntos que hay que tener en cuenta, lo cual no invalida tampoco tus comentarios, solo los complementa.

El primero, el nivel inicial: la idea de los ejemplos que voy mostrando son para eso, para brindar una visión básica, inicial, de cada tema. Como dice el título del Blog, la idea es tratar de que los programadores habituales de PHP (que muchos de ellos solo conocen de programación scripting, estructurada y sin objetos) puedan ir "subiendo de nivel".

Mi idea "pedagógica" es hablar desde "abajo", bajar el nivel, y no empezar desde un "nivel medio/alto", que textos y autores con esa política -en mi humilde opinión- abundan.

Todavía para muchos desarrolladores PHP hablar de patrones de diseño suena a "místico", y si no vienes de Java, hablar de "pool de conexiones" es aún más extraño.

Segundo, el contexto: a pesar que el blog habla de PHP, lo aclaro en este artículo aún más: "desarrollo web, patrón de diseño Singleton, implementado en PHP" (sé claramente que en otras plataformas esto cambia, como tu bien me comentas, no tanto por el contexto sino por como lo han resuelto ellos mismos).

Hay muchos casos y ejemplos, que o los saco del ambiente Java, o hago un paralelismo, o directamente trato de convertirlos o traducirlos al mundo PHP, con las oportunas salvedades, pues hay muchas cosas que se pierden en el camino (PHP al día de hoy sigue siendo un lenguaje y no una plataforma como Java o .Net).

> Las aplicaciones web reciben multiples
> peticiones de forma simultanea, tener
> una sóla conexión abierta con el SGBD
> es un cuello de botella enorme. Otro
> gravisimo problema de este enfoque
> son las transacciones, que se hacen
> a nivel de conexión con la bd.

También estoy de acuerdo; pero creo que tú estarás de acuerdo conmigo que es mejor tener un Singleton reaprovechando una única conexión para una única página (no todo el sistema, por lo que tendríamos varias conexiones al final de cuentas, pero en menor cantidad) que descontroladamente varias conexiones por página por todo el sistema.


> La solución habitual para acceso
> a bd desde paginas web es usar
> un "pool de conexiones", defines
> un número maximo de conexiones
> abiertas y en cada petición le
> solicitas al pool una conexión
> libre. De esta manera controlas
> el número de conexiones maximo,
> no tienes cuellos de botella y
> permites el uso de transacciones.

Si, este tema lo conozco desde el mundo Java, pero al momento no hay nada implementado oficialmente para PHP, y si pudiera existir algo, debería ser un proyecto independiente (pero no tengo el gusto de conocer).

Ahora que tiraste nuevamente el tema al ruedo, voy a ver si investigo sobre este tema para saber si hay algo reciente.

> En java (en .net también) esto
> se hace de forma estándar a
> través de los datasources de
> los servidores de aplicaciones,
> también existen multitud de
> librerías open-source que
> proporcionan pooling.

> En PHP tienen que existir
> soluciones similares.

Como te comentaba, oficialmente no tengo conocimiento. Pero habría que buscar proyectos sobre el tema. De todas formas, empezamos con un ejemplo del Singleton y terminamos "al carajo y más allá" (parafraseando a Toy History ;-)). Pero bien valen tus comentarios porque nos adelantas unos cuantos metros más adelante y esto deja abierto a que hablemos del tema en otros artículos ;-)

Finalmente, en PHP se pueden hacer paralelismos con Java (más ahora que se asemeja PHP5 al lenguaje Java) pero aún estamos años de diferencia en lo que concierne a la plataforma o la arquitectura, aunque se está apuntando a eso, a que se convierta en algo similar a JEE pero mucho más simple.

Muy buenos tus aportes, y espero que para los próximos artículos que vuelva a juntar experiencias entre Java y PHP (muchas veces es mi punto de partida), estés ahí para corregirme, para complementar, o directamente para hacerme ver los disparates que pueda haber cometido y así tener la oportunidad de enmendarme ;-)

Un Hombre Diferente dijo...

todavía no me queda claro que es el tal Singleton.

Rubén Moraleda dijo...

Para un hombre diferente:

Tal vez es un poco tarde para realizar el comentario, pero por si todavía tienes dudas aquí va mi aporte de bajo nivel, espero que te sirva de ayuda.

Piensa en un singleton como una clase de la que sólo puede crearse 1 instancia u objeto, la cual se almacena en una propiedad static de la propia clase durante todo el periodo de ejecución de nuestra aplicación (ya séa via web o via cli).

Por ejemplo: Imagínate por que tenemos la clase "fabrica" que implementa el patrón singleton, es decir, nuestra aplicación sólo tendrá una instancia del objeto fábrica, la cual se encargará de "fabricar" e inicializarnos determinados objetos.

// Inicialización, configuración, etc...
$fabrica=Fabrica::getInstance();
$fabrica->setConfig...

// En otra parte (clase, funcion ....) de nuestra aplicación podriamos llamar al objeto fábrica para utilizar sus métodos, ésta ya se encuentra inicializada y configurada ya que lo hicimos previamente:

$fabrica=Fabrica::getInstance();
$motocicleta=$fabrica->nuevoVehiculo(Fabrica::VEHICULO_MOTOCICLETA);
$automovil=$fabrica->nuevoVehiculo(Fabrica::VEHICULO_AUTOMOVIL);

Espero no haber liado más las cosas :)

Saludos.

Entradas populares