¿Global o Singleton para la conexión a la base de datos?


81

¿Cuál es el beneficio de usar singleton en lugar de global para conexiones de base de datos en PHP? Siento que usar singleton en lugar de global hace que el código sea innecesariamente complejo.

Codificar con Global

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

Código con Singleton

class DB_Instance
{
    private static $db;

    public static function getDBO()
    {
        if (!self::$db)
            self::$db = new PDO(...);

        return self::$db;
    }
}

function getSomething()
{
    $conn = DB_Instance::getDBO();
    .
    .
    .
}

Si hay una mejor forma de inicializar la conexión de base de datos que no sea global o singleton, por favor menciónela y describa las ventajas que tiene sobre global o singleton.


Si planea usar PDO en controladores de sesión personalizados, debe tener en cuenta algunas peculiaridades: stackoverflow.com/questions/2595860/pdo-prepare-silently-fails
Wabbitseason

Respuestas:


105

Sé que esto es antiguo, pero la respuesta de Dr8k casi estaba ahí.

Cuando esté considerando escribir un fragmento de código, asuma que va a cambiar. Eso no significa que esté asumiendo el tipo de cambios que habrá generado en algún momento en el futuro, sino que se hará algún tipo de cambio.

Conviértalo en un objetivo para mitigar el dolor de hacer cambios en el futuro: un mundo global es peligroso porque es difícil de administrar en un solo lugar. ¿Qué sucede si quiero que esa conexión de base de datos sea consciente del contexto en el futuro? ¿Qué pasa si quiero que se cierre y se vuelva a abrir cada quinta vez que se usa? ¿Qué pasa si decido que, en aras de escalar mi aplicación, quiero usar un grupo de 10 conexiones? ¿O un número configurable de conexiones?

Una fábrica de singleton le brinda esa flexibilidad. Lo configuro con muy poca complejidad adicional y obtengo más que solo acceso a la misma conexión; Gano la capacidad de cambiar la forma en que se me pasa esa conexión más adelante de una manera simple.

Tenga en cuenta que digo singleton factory en lugar de simplemente singleton . Hay muy poca diferencia entre un singleton y un global, cierto. Y debido a eso, no hay razón para tener una conexión singleton: ¿por qué dedicarías el tiempo a configurar eso cuando podrías crear un global regular en su lugar?

Lo que le ofrece una fábrica es un por qué obtener conexiones y un lugar separado para decidir qué conexiones (o conexión) obtendrá.

Ejemplo

class ConnectionFactory
{
    private static $factory;
    private $db;

    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    public function getConnection() {
        if (!$this->db)
            $this->db = new PDO(...);
        return $this->db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

Luego, en 6 meses, cuando su aplicación sea súper famosa y se esté volviendo loca y usted decida que necesita más de una conexión, todo lo que tiene que hacer es implementar algunas agrupaciones en el método getConnection (). O si decide que quiere un contenedor que implemente el registro SQL, puede pasar una subclase PDO. O si decide que quiere una nueva conexión en cada invocación, puede hacerlo. Es flexible, en lugar de rígido.

16 líneas de código, incluidas llaves, que le ahorrarán horas y horas y horas de refactorización a algo inquietantemente similar en el futuro.

Tenga en cuenta que no considero esto "Feature Creep" porque no estoy implementando ninguna característica en la primera ronda. Es la línea fronteriza "Future Creep", pero en algún momento, la idea de que "programar para el mañana hoy" es siempre algo malo no me gusta.


3
No estoy seguro, pero creo que quiso decir: public function getConnection () {if (! $ This-> db) $ this-> db = new PDO (...); return $ this-> db; }
Dycey

¡Gracias! ¿Perdería alguno de los beneficios de usar este método usando en return self::$factory->getConnection();lugar de return self::$factory;?
Nico Burns

3
Me gustaría usar este código en un proyecto que estoy haciendo. ¿Puedo citar esta página y, de ser así, bajo qué licencia se encuentra este texto? ¿Es CC-BY, BSD o algo más? Actualmente he reclamado esto como "Desconocido - cree dominio público" pero me gustaría atribuirle las condiciones de licencia correctas.
JonTheNiceGuy

2
Creo que deberíamos hacer "$ db" "$ this-> db" en el método getConnection (), de lo contrario "la variable" private $ db "" no se usa ", que no está oficialmente referenciada en ninguna parte.
desarrollador10

Hola, tu solución se ve genial y escalable. Por favor, avíseme qué debe implementarse aquí self :: $ factory = new ConnectionFactory (...);
Ananda

16

No estoy seguro de poder responder a su pregunta específica, pero quería sugerir que los objetos de conexión global / singleton pueden no ser la mejor idea si se trata de un sistema basado en web. Los DBMS generalmente están diseñados para administrar un gran número de conexiones únicas de manera eficiente. Si está utilizando un objeto de conexión global, entonces está haciendo un par de cosas:

  1. Obligar a sus páginas a hacer todas las conexiones de base de datos secuencialmente y eliminar cualquier intento de carga de páginas asíncronas

  2. Potencialmente mantener los bloqueos abiertos en los elementos de la base de datos más de lo necesario, lo que ralentiza el rendimiento general de la base de datos.

  3. Maximizar el número total de conexiones simultáneas que su base de datos puede admitir y bloquear el acceso de nuevos usuarios a los recursos.

Estoy seguro de que también hay otras posibles consecuencias. Recuerde, este método intentará mantener una conexión de base de datos para cada usuario que acceda al sitio. Si solo tiene uno o dos usuarios, no hay problema. Si se trata de un sitio web público y desea tráfico, la escalabilidad se convertirá en un problema.

[EDITAR]

En situaciones de mayor escala, crear nuevas conexiones cada vez que acceda a la base de datos puede ser malo. Sin embargo, la respuesta no es crear una conexión global y reutilizarla para todo. La respuesta es la agrupación de conexiones.

Con la agrupación de conexiones, se mantienen varias conexiones distintas. Cuando la aplicación requiere una conexión, se recupera la primera conexión disponible del grupo y luego se devuelve al grupo una vez que se realiza su trabajo. Si se solicita una conexión y no hay ninguna disponible, sucederá una de estas dos cosas: a) si no se alcanza el número máximo de conexiones permitidas, se abre una nueva conexión, ob) la aplicación se ve obligada a esperar a que una conexión esté disponible .

Nota: En los lenguajes .Net, la agrupación de conexiones es manejada por los objetos ADO.Net por defecto (la cadena de conexión establece toda la información requerida).

Gracias a Crad por comentar esto.


Supongo que usar singleton me da la ventaja de inicializar la conexión lo más tarde posible, mientras que si uso global, probablemente la inicializaría casi al principio del script. ¿Estoy en lo correcto?
Imran

Correcto, si tomara este camino, el singleton le proporcionaría esa ventaja.
Dr8k

En realidad, todo depende de la escala. En implementaciones web a gran escala, cantidades masivas de conexiones DB son malas. Es por eso que existen aplicaciones como pgBouncer para PostgreSQL y Java hace la agrupación de recursos.
Gavin M. Roy

Es cierto, pero creo que la agrupación de conexiones adecuada es diferente a simplemente usar objetos de conexión global. La agrupación de conexiones todavía usa múltiples conexiones, solo limita el número máximo y las vuelve a usar con el tiempo para aliviar la sobrecarga de configuración.
Dr8k

5
Tenga en cuenta que "global" en el caso de PHP no hace que la variable sea global en las páginas PHP. Simplemente significa que se puede acceder a él desde funciones.
Ates Goral

7

El método singleton se creó para asegurarse de que solo hubiera una instancia de cualquier clase. Pero, debido a que la gente lo usa como una forma de atajar la globalización, se lo conoce como programación perezosa y / o mala.

Por lo tanto, ignoraría global y Singleton ya que ambos no son realmente OOP.

Lo que buscabas es una inyección de dependencia .

Puede consultar información fácil de leer basada en PHP relacionada con la inyección de dependencia (con ejemplos) en http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection


3

Ambos patrones logran el mismo efecto de red, proporcionando un único punto de acceso para sus llamadas a la base de datos.

En términos de implementación específica, el singleton tiene una pequeña ventaja de no iniciar una conexión a la base de datos hasta que al menos uno de sus otros métodos lo solicite. En la práctica, en la mayoría de las aplicaciones que he escrito, esto no hace mucha diferencia, pero es una ventaja potencial si tiene algunas páginas / rutas de ejecución que no realizan ninguna llamada a la base de datos, ya que esas páginas no lo harán. alguna vez solicite una conexión a la base de datos.

Otra pequeña diferencia es que la implementación global puede pisotear otros nombres de variables en la aplicación sin querer. Es poco probable que alguna vez declare accidentalmente otra referencia global $ db, aunque es posible que pueda sobrescribirla accidentalmente (digamos, escribe if ($ db = null) cuando quería escribir if ($ db == null). El objeto singleton evita eso.


2

Si no va a utilizar una conexión persistente, y hay casos para no hacerlo, creo que un singleton es conceptualmente más aceptable que un global en el diseño OO.

En una verdadera arquitectura OO, un singleton es más efectivo que crear una nueva instancia del objeto cada vez.


2

En el ejemplo dado, no veo ninguna razón para usar singletons. Como regla general, si mi única preocupación es permitir una sola instancia de un objeto, si el lenguaje lo permite, prefiero usar globales


1

En general, usaría un singleton para una conexión de base de datos ... No desea crear una nueva conexión cada vez que necesite interactuar con la base de datos ... Esto podría afectar el rendimiento y el ancho de banda de su red ... ¿Por qué crear una uno nuevo, cuando hay uno disponible ... Solo mis 2 centavos ...

RWendi


Al inicializar la conexión en el alcance global, estoy inicializando la conexión una vez por página y usando esa variable global en funciones que necesitan interactuar con la base de datos.
Imran

Usar un singleton para una conexión de base de datos no es lo mismo que no volver a crear la conexión en cada interacción con el DBMS. Un singleton es solo eso; una instancia de una clase determinada, y la única a la que se le permite existir globalmente. Es posible que deba conectarse a diferentes bases de datos a la vez.
Rob

Me estaba refiriendo a una clase singleton que administra las conexiones a la base de datos. No veo el punto de crear un nuevo objeto de conexión cada vez que desee interactuar con un dbms. Por supuesto, si necesita conectarse a una base de datos diferente a la vez, es posible que deba crear otro objeto de conexión.
RWendi

0

Es muy sencillo. Nunca use OR Singleton global.


4
Cuando se trata del patrón Singleton, siempre diga nunca
1800 INFORMACIÓN

3
¿Qué sucede si desea más de un proveedor de registros? ¿Quién dice que no puedo iniciar sesión en un archivo y en la consola?
1800 INFORMACIÓN

2
Entonces, combinaría un anti-patrón (Singleton) con otro (Objeto de Dios)
1800 INFORMACIÓN

3
Si. Y también lo hacen los diseñadores de todos los principales lenguajes de programación orientada a objetos, al permitir objetos de clase global. Si eres monoteísta y quieres un objeto que represente a Dios, entonces lo que estás buscando es un Objeto de Dios Singleton. Cualquier otra cosa es incorrecta.
Steve Jessop

4
Si yo fuera un montheísta y quisiera crear un objeto de Dios, ¿podría especificar usar un patrón MonotheistAbstractFactory para crear mi Objeto de Dios? Esto dejaría abierta la posibilidad de que los usuarios politeístas también usen mi programa especificando una PolytheistAbstractfactory.
1800 INFORMACIÓN

0

Como consejo tanto singleton como global son válidos y se pueden unir dentro de un mismo sistema, proyecto, plugin, producto, etc ... En mi caso, hago productos digitales para web (plugin).

Solo uso singleton en la clase principal y lo uso por principio. Casi no lo uso porque sé que la clase principal no lo instanciará nuevamente

<?php // file0.php

final class Main_Class
{
    private static $instance;
    private $time;

    private final function __construct()
    {
        $this->time = 0;
    }
    public final static function getInstance() : self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }
    public final function __clone()
    {
        throw new LogicException("Cloning timer is prohibited");
    }
    public final function __sleep()
    {
        throw new LogicException("Serializing timer is prohibited");
    }
    public final function __wakeup()
    {
        throw new LogicException("UnSerializing timer is prohibited");
    }
}

Uso global para casi todas las clases secundarias, ejemplo:

<?php // file1.php
global $YUZO;
$YUZO = new YUZO; // YUZO is name class

mientras que en tiempo de ejecución puedo usar Global para llamar a sus métodos y atributos en la misma instancia porque no necesito otra instancia de mi clase de producto principal.

<?php // file2.php
global $YUZO;
$YUZO->method1()->run();
$YUZO->method2( 'parameter' )->html()->print();

Lo que obtengo con el global es usar la misma instancia para poder hacer que el producto funcione porque no necesito una fábrica para instancias de la misma clase, generalmente la fábrica de instancias es para sistemas grandes o para propósitos muy raros.

In conclusion:, debes si ya entiendes bien que es el anti-patrón Singleton y entiendes el Global , puedes usar una de las 2 opciones o mezclarlas pero si te recomiendo no abusar ya que hay muchos programadores que son muy excepcionales y fieles a la programación OOP, úsala para las clases principales y secundarias que usas mucho dentro del tiempo de ejecución. (Te ahorra mucha CPU). 😉

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.