Global oder Singleton für Datenbankverbindung?

81

Was ist der Vorteil der Verwendung von Singleton anstelle von Global für Datenbankverbindungen in PHP? Ich bin der Meinung, dass die Verwendung von Singleton anstelle von Global den Code unnötig komplex macht.

Code mit Global

$conn = new PDO(...);

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

Code mit 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();
    .
    .
    .
}

Wenn es eine bessere Möglichkeit gibt, eine andere Datenbankverbindung als global oder singleton zu initialisieren, erwähnen Sie diese bitte und beschreiben Sie die Vorteile gegenüber global oder singleton.

Imran
quelle
Wenn Sie planen, PDO bei benutzerdefinierten Sitzungshandlern zu verwenden, sollten Sie einige Besonderheiten beachten
Wabbitseason

Antworten:

105

Ich weiß, dass dies alt ist, aber Dr8ks Antwort war fast da.

Wenn Sie überlegen, einen Code zu schreiben, gehen Sie davon aus, dass sich dieser ändern wird. Das bedeutet nicht, dass Sie davon ausgehen, welche Art von Änderungen irgendwann in der Zukunft vorgenommen werden, sondern dass irgendeine Form von Änderung vorgenommen wird.

Machen Sie es zu einem Ziel, das den Schmerz lindert, Änderungen in der Zukunft vorzunehmen: Ein globales Unternehmen ist gefährlich, weil es schwierig ist, es an einem einzigen Ort zu verwalten. Was ist, wenn ich diesen Datenbankverbindungskontext in Zukunft bewusst machen möchte? Was ist, wenn ich möchte, dass es sich jedes Mal, wenn es verwendet wurde, schließt und wieder öffnet? Was ist, wenn ich im Interesse der Skalierung meiner App einen Pool von 10 Verbindungen verwenden möchte? Oder eine konfigurierbare Anzahl von Verbindungen?

Eine Singleton-Fabrik bietet Ihnen diese Flexibilität. Ich habe es mit sehr wenig zusätzlicher Komplexität eingerichtet und erhalte mehr als nur Zugriff auf dieselbe Verbindung. Ich kann auf einfache Weise ändern, wie diese Verbindung später an mich weitergegeben wird.

Beachten Sie, dass ich Singleton Factory im Gegensatz zu einfach Singleton sage . Es gibt kaum einen Unterschied zwischen einem Singleton und einem globalen. Aus diesem Grund gibt es keinen Grund für eine Singleton-Verbindung: Warum sollten Sie die Zeit damit verbringen, diese einzurichten, wenn Sie stattdessen eine reguläre globale Verbindung erstellen könnten?

Was eine Fabrik Ihnen bringt, ist ein Grund, warum Sie Verbindungen erhalten, und ein separater Ort, an dem Sie entscheiden können, welche Verbindungen (oder Verbindungen) Sie erhalten.

Beispiel

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();
    .
    .
    .
}

Dann, in 6 Monaten, wenn Ihre App sehr berühmt ist und durcheinander und mit einem Schrägstrich versehen wird und Sie entscheiden, dass Sie mehr als eine einzige Verbindung benötigen, müssen Sie nur ein Pooling in der Methode getConnection () implementieren. Wenn Sie sich für einen Wrapper entscheiden, der die SQL-Protokollierung implementiert, können Sie eine PDO-Unterklasse übergeben. Oder wenn Sie bei jedem Aufruf eine neue Verbindung wünschen, können Sie dies tun. Es ist flexibel statt starr.

16 Codezeilen, einschließlich geschweifter Klammern, ersparen Ihnen Stunden und Stunden und Stunden des Refactorings für etwas Unheimliches auf der ganzen Linie.

Beachten Sie, dass ich dieses "Feature Creep" nicht berücksichtige, da ich in der ersten Runde keine Feature-Implementierung durchführe. Es ist die Grenzlinie "Future Creep", aber irgendwann ist die Idee, dass "Codierung für morgen heute" immer eine schlechte Sache ist, für mich nicht lebendig.

Jon Raphaelson
quelle
3
Ich bin mir nicht sicher, aber ich denke, Sie meinten: öffentliche Funktion getConnection () {if (! $ This-> db) $ this-> db = new PDO (...); return $ this-> db; }
Dycey
Vielen Dank! Würde ich einen der Vorteile dieser Methode verlieren, wenn ich sie return self::$factory->getConnection();anstelle von verwende return self::$factory;?
Nico Burns
3
Ich möchte diesen Code in einem Projekt verwenden, das ich gerade mache. Kann ich diese Seite zitieren und wenn ja, unter welcher Lizenz befindet sich dieser Text? Ist es CC-BY, BSD oder etwas anderes? Ich habe dies derzeit als "Unbekannt - glaube Public Domain" behauptet, möchte aber die korrekten Lizenzbedingungen darauf zurückführen.
JonTheNiceGuy
2
Ich denke, wir sollten "$ db" "$ this-> db" in der getConnection () -Methode machen, sonst wird "private $ db" -Variable "nicht verwendet", auf die nirgendwo offiziell verwiesen wird.
Entwickler10
Hallo, deine Lösung sieht cool und skalierbar aus. Bitte lassen Sie mich wissen, was hier implementiert werden muss self :: $ factory = new ConnectionFactory (...);
Ananda
16

Ich bin nicht sicher, ob ich Ihre spezifische Frage beantworten kann, wollte aber vorschlagen, dass globale / Singleton-Verbindungsobjekte möglicherweise nicht die beste Idee sind, wenn dies für ein webbasiertes System gilt. DBMS sind im Allgemeinen so konzipiert, dass sie eine große Anzahl eindeutiger Verbindungen auf effiziente Weise verwalten. Wenn Sie ein globales Verbindungsobjekt verwenden, gehen Sie wie folgt vor:

  1. Erzwingen Sie, dass Ihre Seiten alle Datenbankverbindungen nacheinander ausführen, und beenden Sie alle Versuche, asynchrone Seiten zu laden.

  2. Möglicherweise länger als nötig offene Sperren für Datenbankelemente halten, was die Gesamtleistung der Datenbank verlangsamt.

  3. Maximierung der Gesamtzahl gleichzeitiger Verbindungen, die Ihre Datenbank unterstützen kann, und Verhinderung des Zugriffs neuer Benutzer auf die Ressourcen.

Ich bin sicher, dass es auch andere mögliche Konsequenzen gibt. Denken Sie daran, dass diese Methode versucht, eine Datenbankverbindung für jeden Benutzer aufrechtzuerhalten, der auf die Site zugreift. Wenn Sie nur einen oder zwei Benutzer haben, ist dies kein Problem. Wenn dies eine öffentliche Website ist und Sie Datenverkehr wünschen, wird die Skalierbarkeit zu einem Problem.

[BEARBEITEN]

In größeren Situationen kann es schlecht sein, jedes Mal, wenn Sie auf die Datenbank treffen, neue Verbindungen herzustellen. Die Antwort besteht jedoch nicht darin, eine globale Verbindung herzustellen und für alles wiederzuverwenden. Die Antwort lautet Verbindungspooling.

Beim Verbindungspooling werden mehrere unterschiedliche Verbindungen aufrechterhalten. Wenn die Anwendung eine Verbindung benötigt, wird die erste verfügbare Verbindung aus dem Pool abgerufen und nach Abschluss der Aufgabe an den Pool zurückgegeben. Wenn eine Verbindung angefordert wird und keine verfügbar ist, geschieht eines von zwei Dingen: a) Wenn die maximal zulässige Anzahl von Verbindungen nicht erreicht wird, wird eine neue Verbindung geöffnet oder b) Die Anwendung muss warten, bis eine Verbindung verfügbar ist .

Hinweis: In .NET-Sprachen wird das Verbindungspooling standardmäßig von den ADO.Net-Objekten ausgeführt (die Verbindungszeichenfolge legt alle erforderlichen Informationen fest).

Vielen Dank an Crad für den Kommentar.

Dr8k
quelle
Ich denke, die Verwendung von Singleton bietet mir den Vorteil, die Verbindung so spät wie möglich zu initialisieren. Wenn ich global verwende, würde ich wahrscheinlich fast am Anfang des Skripts initialisieren. Hab ich recht?
Imran
Richtig, wenn Sie diesen Weg gehen würden, würde der Singleton diesen Vorteil bieten.
Dr8k
Eigentlich hängt das alles vom Maßstab ab. In großen Webbereitstellungen sind riesige Mengen an DB-Verbindungen böse. Aus diesem Grund gibt es Apps wie pgBouncer für PostgreSQL und Java führt Ressourcenpooling durch.
Gavin M. Roy
Stimmt, aber ich denke, dass ein ordnungsgemäßes Verbindungspooling sich von der Verwendung globaler Verbindungsobjekte unterscheidet. Beim Verbindungspooling werden immer noch mehrere Verbindungen verwendet. Es wird lediglich die maximale Anzahl begrenzt und im Laufe der Zeit wiederverwendet, um den Einrichtungsaufwand zu verringern.
Dr8k
5
Beachten Sie, dass "global" im Fall von PHP die Variable nicht über PHP-Seiten hinweg global macht. Es bedeutet nur, dass innerhalb von Funktionen darauf zugegriffen werden kann.
Ates Goral
7

Die Singleton-Methode wurde erstellt, um sicherzustellen, dass nur eine Instanz einer Klasse vorhanden ist. Aber weil die Leute es als Mittel zur Abkürzung der Globalisierung verwenden, wird es als faule und / oder schlechte Programmierung bekannt.

Daher würde ich Global und Singleton ignorieren, da beide nicht wirklich OOP sind.

Was Sie gesucht haben, ist die Abhängigkeitsinjektion .

Unter http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection finden Sie einfach zu lesende PHP-basierte Informationen zur Abhängigkeitsinjektion (mit Beispielen)


quelle
3

Beide Muster erzielen den gleichen Nettoeffekt und bieten einen einzigen Zugriffspunkt für Ihre Datenbankaufrufe.

In Bezug auf die spezifische Implementierung hat der Singleton den kleinen Vorteil, dass keine Datenbankverbindung initiiert wird, bis mindestens eine Ihrer anderen Methoden dies anfordert. In der Praxis macht dies in den meisten Anwendungen, die ich geschrieben habe, keinen großen Unterschied, aber es ist ein potenzieller Vorteil, wenn Sie einige Seiten / Ausführungspfade haben, die überhaupt keine Datenbankaufrufe ausführen, da diese Seiten dies nicht tun Fordern Sie jemals eine Verbindung zur Datenbank an.

Ein weiterer kleiner Unterschied besteht darin, dass die globale Implementierung möglicherweise unbeabsichtigt andere Variablennamen in der Anwendung mit Füßen tritt. Es ist unwahrscheinlich, dass Sie jemals versehentlich eine andere globale $ db-Referenz deklarieren, obwohl es möglich ist, dass Sie diese versehentlich überschreiben (sagen Sie, Sie schreiben if ($ db = null), wenn Sie if ($ db == null) schreiben wollten. Das Singleton-Objekt verhindert dies.

Adam Ness
quelle
2

Wenn Sie keine dauerhafte Verbindung verwenden und es Fälle gibt, in denen dies nicht der Fall ist, finde ich einen Singleton konzeptionell schmackhafter als einen globalen OO-Entwurf.

In einer echten OO-Architektur ist ein Singleton effektiver, als jedes Mal eine neue Instanz des Objekts zu erstellen.

Gavin M. Roy
quelle
2

In dem gegebenen Beispiel sehe ich keinen Grund, Singletons zu verwenden. Als Faustregel gilt, wenn es mein einziges Anliegen ist, eine einzelne Instanz eines Objekts zuzulassen. Wenn die Sprache dies zulässt, bevorzuge ich die Verwendung von Globalen

Dprado
quelle
1

Im Allgemeinen würde ich einen Singleton für eine Datenbankverbindung verwenden ... Sie möchten nicht jedes Mal eine neue Verbindung erstellen, wenn Sie mit der Datenbank interagieren müssen ... Dies kann die Leistung und Bandbreite Ihres Netzwerks beeinträchtigen ... Warum sollten Sie eine erstellen? neue, wenn es eine gibt ... Nur meine 2 Cent ...

RWendi

RWendi
quelle
Wenn ich die Verbindung im globalen Bereich initialisiere, initialisiere ich die Verbindung einmal pro Seite und verwende diese globale Variable in Funktionen, die mit der Datenbank interagieren müssen.
Imran
Die Verwendung eines Singletons für eine Datenbankverbindung ist nicht gleichbedeutend damit, dass die Verbindung nicht bei jeder Interaktion mit dem DBMS neu erstellt wird. Ein Singleton ist genau das; eine Instanz einer bestimmten Klasse und die einzige, die global existieren darf. Möglicherweise müssen Sie gleichzeitig eine Verbindung zu verschiedenen Datenbanken herstellen.
Rob
Ich bezog mich auf eine Singleton-Klasse, die Verbindungen zur Datenbank verwaltet. Ich sehe keinen Sinn darin, jedes Mal ein neues Verbindungsobjekt zu erstellen, wenn Sie mit einer Datenbank interagieren möchten. Wenn Sie gleichzeitig eine Verbindung zu einer anderen Datenbank herstellen müssen, müssen Sie möglicherweise ein anderes Verbindungsobjekt erstellen.
RWendi
0

Es ist ganz einfach. Verwenden Sie niemals globales ODER Singleton.

1800 INFORMATIONEN
quelle
4
Wenn es um Singleton-Muster geht, sagen Sie immer nie
1800 INFORMATION
3
Was ist, wenn Sie mehr als einen Protokollanbieter möchten? Wer sagt, dass ich mich nicht bei einer Datei und der Konsole anmelden kann?
1800 INFORMATION
2
Sie würden also ein Anti-Pattern (Singleton) mit einem anderen (God Object) kombinieren
1800 INFORMATION
3
Ja. Dies gilt auch für die Designer aller wichtigen OOP-Sprachen, indem sie globale Klassenobjekte zulassen. Wenn Sie ein Monotheist sind und möchten, dass ein Objekt Gott darstellt, suchen Sie nach einem Singleton-Gott-Objekt. Alles andere ist falsch.
Steve Jessop
4
Wenn ich ein Montheist wäre und ein Gottobjekt erstellen wollte, könnte ich angeben, dass ich ein MonotheistAbstractFactory-Muster verwenden möchte, um mein Gottobjekt zu erstellen. Dies würde polytheistischen Benutzern die Möglichkeit offen lassen, mein Programm auch durch Angabe einer PolytheistAbstractfactory zu verwenden.
1800 INFORMATION
0

Als Ratschlag sind sowohl Singleton als auch Global gültig und können innerhalb desselben Systems, Projekts, Plugins, Produkts usw. zusammengeführt werden . In meinem Fall mache ich digitale Produkte für das Web (Plugin).

Ich benutze nur Singleton in der Hauptklasse und ich benutze es grundsätzlich. Ich benutze es fast nicht, weil ich weiß, dass die Hauptklasse es nicht wieder instanziieren wird

<?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");
    }
}

Globale Verwendung für fast alle Sekundärklassen, Beispiel:

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

Zur Laufzeit kann ich Global verwenden , um ihre Methoden und Attribute in derselben Instanz aufzurufen, da ich keine weitere Instanz meiner Hauptproduktklasse benötige.

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

Ich bekomme mit dem globalen Ziel, dieselbe Instanz zu verwenden, um das Produkt zum Laufen zu bringen, da ich für Instanzen derselben Klasse keine Factory benötige. Normalerweise ist die Instanzfactory für große Systeme oder für sehr seltene Zwecke.

In conclusion:Wenn Sie bereits gut verstehen, dass es sich um das Anti-Pattern- Singleton handelt, und das Global verstehen , können Sie eine der beiden Optionen verwenden oder sie mischen. Wenn ich jedoch empfehle, sie nicht zu missbrauchen, gibt es viele Programmierer, die sehr außergewöhnlich und treu sind Verwenden Sie die Programmier-OOP für Haupt- und Sekundärklassen, die Sie innerhalb der Ausführungszeit häufig verwenden. (Es spart Ihnen viel CPU). 😉

Lenin Zapata
quelle