データベース接続のグローバルまたはシングルトン?


81

PHPのデータベース接続にグローバルではなくシングルトンを使用する利点は何ですか?グローバルの代わりにシングルトンを使用すると、コードが不必要に複雑になると思います。

グローバルなコード

$conn = new PDO(...);

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

シングルトンを使用したコード

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

グローバルまたはシングルトン以外のデータベース接続を初期化するためのより良い方法がある場合は、それについて言及し、グローバルまたはシングルトンよりも優れている点を説明してください。


カスタムセッションハンドラーでPDOを使用することを計画している場合は、いくつかの特殊性に注意する必要があります:stackoverflow.com/questions/2595860/pdo-prepare-silently-fails
Wabbitseason 2010年

回答:


105

これは古いことは知っていますが、Dr8kの答えはほとんどありませんでした。

コードの記述を検討しているときは、それが変更されると想定してください。これは、将来のある時点でそれがもたらす変更の種類を想定しているという意味ではなく、何らかの形の変更が行われるという意味です。

将来の変更の苦痛を軽減することを目標にします。グローバルは、単一の場所で管理するのが難しいため、危険です。将来、そのデータベース接続コンテキストを認識させたい場合はどうすればよいですか?5回使用するたびに閉じてから再度開くようにするにはどうすればよいですか。アプリをスケーリングするために、10個の接続のプールを使用することにした場合はどうなりますか?または、構成可能な接続数ですか?

シングルトンの工場は、あなたにその柔軟性を提供します。余分な複雑さをほとんど伴わずにセットアップし、同じ接続にアクセスするだけでは不十分です。その接続が後で簡単な方法で私に渡される方法を変更する機能を取得します。

単にシングルトンではなく、シングルトンファクトリと言っていることに注意してください。シングルトンとグローバルの間には、貴重な小さな違いがあります。そのため、シングルトン接続を使用する理由はありません。代わりに通常のグローバルを作成できるのに、なぜその設定に時間を費やすのでしょうか。

ファクトリが取得するのは、接続を取得する理由であり、取得する接続(または接続)を決定するための別の場所です。

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

次に、アプリが非常に有名になり、スラッシュドット効果が発生し、複数の接続が必要であると判断した6か月後に、getConnection()メソッドにプーリングを実装するだけで済みます。または、SQLロギングを実装するラッパーが必要であると判断した場合は、PDOサブクラスを渡すことができます。または、呼び出しごとに新しい接続が必要であると判断した場合は、それを行うことができます。剛性ではなく柔軟性があります。

中括弧を含む16行のコード。これにより、不気味に似たようなものにリファクタリングする時間を節約できます。

最初のラウンドでは機能の実装を行っていないため、この「機能クリープ」は考慮しないことに注意してください。それは「FutureCreep」の境界線ですが、ある時点で、「今日の明日のためのコーディング」が常に悪いことであるという考えは私にとっては気になりません。


3
わかりませんが、次のことを意味していると思います。publicfunction getConnection(){if(!$ this-> db)$ this-> db = new PDO(...); $ this-> dbを返します。}
Dycey 2010

ありがとう!return self::$factory->getConnection();代わりにを使用することで、この方法を使用する利点は失われreturn self::$factory;ますか?
ニコバーンズ

3
私が行っているプロジェクトでこのコードを使用したいと思います。このページを引用できますか?もしそうなら、このテキストはどのライセンスの下にありますか?CC-BY、BSD、または他の何かですか?私は現在これを「不明-パブリックドメインを信じる」と主張していますが、正しいライセンス条件を付けたいと思います。
JonTheNiceGuy 2011年

2
getConnection()メソッドで "$ db" "$ this-> db"を作成する必要があると思います。そうしないと、公式に参照されていない "private $ db"変数 "が使用されません"。
developer10

こんにちはあなたのソリューションはクールでスケーラブルに見えます。ここで実装する必要があるものを教えてくださいself :: $ factory = new ConnectionFactory(...);
アナンダ2015年

16

あなたの特定の質問に答えられるかどうかはわかりませんが、グローバル/シングルトン接続オブジェクトがWebベースのシステムの場合、これが最善のアイデアではない可能性があることを示唆したいと思います。DBMSは通常、効率的な方法で多数の一意の接続を管理するように設計されています。グローバル接続オブジェクトを使用している場合は、いくつかのことを行っています。

  1. ページにすべてのデータベース接続を順番に実行させ、非同期ページロードの試行をすべて強制終了します。

  2. データベース要素のオープンロックを必要以上に長く保持する可能性があり、データベース全体のパフォーマンスが低下します。

  3. データベースがサポートできる同時接続の総数を最大化し、新しいユーザーがリソースにアクセスするのをブロックします。

他にも潜在的な結果があると確信しています。この方法では、サイトにアクセスするすべてのユーザーのデータベース接続を維持しようとすることを忘れないでください。ユーザーが1人か2人しかない場合は、問題ありません。これが公開Webサイトであり、トラフィックが必要な場合は、スケーラビリティが問題になります。

[編集]

大規模な状況では、データにアクセスするたびに新しい接続を作成するのは悪い場合があります。ただし、答えは、グローバル接続を作成してすべてに再利用することではありません。答えは接続プーリングです。

接続プーリングを使用すると、多数の個別の接続が維持されます。アプリケーションが接続を必要とする場合、プールから最初に使用可能な接続が取得され、ジョブが完了するとプールに戻されます。接続が要求され、使用可能なものがない場合、次の2つのいずれかが発生します。a)許可された接続の最大数に達しない場合、新しい接続が開かれる、またはb)アプリケーションが接続が使用可能になるのを強制的に待機する。

注: .Net言語では、接続プールはデフォルトでADO.Netオブジェクトによって処理されます(接続文字列は必要なすべての情報を設定します)。

これについてコメントしてくれたCradに感謝します。


シングルトンを使用すると、接続をできるだけ遅く初期化できるという利点があると思いますが、グローバルを使用する場合は、スクリプトのほぼ最初に初期化する可能性があります。私は正しいですか?
イムラン

正解です。この道を進むとしたら、シングルトンがその利点をもたらします。
dr8k 2008

実際、それはすべて規模に依存します。大規模なWeb展開では、大量のDB接続は悪です。これが、pgBouncerのようなアプリがPostgreSQL用に存在し、Javaがリソースプーリングを行う理由です。
ギャビンM.ロイ

本当ですが、適切な接続プーリングは、グローバル接続オブジェクトを使用することとは異なると思います。接続プーリングは引き続き複数の接続を使用します。最大数を制限し、時間の経過とともにそれらを再利用して、セットアップのオーバーヘッドを軽減します。
Dr8k 2008

5
PHPの場合の「グローバル」は、PHPページ全体で変数をグローバルにしないことに注意してください。これは、関数内からアクセスできることを意味します。
Ates Goral

7

シングルトンメソッドは、クラスのインスタンスが1つだけであることを確認するために作成されました。しかし、人々はそれをグローバル化をショートカットする方法として使用するため、怠惰なプログラミングや悪いプログラミングとして知られるようになります。

したがって、グローバルとシングルトンはどちらも実際にはOOPではないため、無視します。

あなたが探していたのは依存性注入です。

http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injectionで、依存性注入に関連する読みやすいPHPベースの情報(例を含む)を確認できます。


3

どちらのパターンも同じ正味の効果を実現し、データベース呼び出しに1つのアクセスポイントを提供します。

特定の実装に関して、シングルトンには、他のメソッドの少なくとも1つがデータベース接続を要求するまで、データベース接続を開始しないという小さな利点があります。実際には、私が作成したほとんどのアプリケーションでは、これは大きな違いにはなりませんが、データベース呼び出しをまったく行わないページ/実行パスがある場合、それらのページはそうしないため、潜在的な利点になります。データベースへの接続を要求することはありません。

もう1つの小さな違いは、グローバル実装がアプリケーション内の他の変数名を意図せず踏みにじる可能性があることです。別のグローバル$ db参照を誤って宣言する可能性はほとんどありませんが、誤って上書きする可能性があります(たとえば、if($ db == null)を書き込むつもりのときにif($ db = null)を記述します。シングルトンオブジェクトはそれを防ぎます。


2

持続的接続を使用する予定がなく、使用しない場合がある場合は、オブジェクト指向設計のグローバルよりも概念的にシングルトンの方が口に合うと思います。

真のオブジェクト指向アーキテクチャでは、オブジェクトごとに新しいインスタンスを作成するよりも、シングルトンの方が効果的です。


2

与えられた例では、シングルトンを使用する理由はわかりません。経験則として、私の唯一の関心事がオブジェクトの単一インスタンスを許可することである場合、言語がそれを許可する場合、私はグローバルを使用することを好みます


1

一般に、データベース接続にはシングルトンを使用します...データベースと対話する必要があるたびに新しい接続を作成する必要はありません...これにより、ネットワークのパフォーマンスと帯域幅が損なわれる可能性があります...なぜ作成するのか新しいもの、利用可能なものがあるとき...ちょうど私の2セント...

RWendi


グローバルスコープで接続を初期化することで、ページごとに1回接続を初期化し、データベースと対話する必要がある関数でそのグローバル変数を使用します。
イムラン

データベース接続にシングルトンを使用することは、DBMSとの対話のたびに接続を再作成しないことと同じではありません。シングルトンはまさにそれです。特定のクラスの1つのインスタンスであり、グローバルに存在できるのは1つだけです。一度に別のデータベースに接続する必要がある場合があります。
ロブ

データベースへの接続を管理するシングルトンクラスを参照していました。dbmsと対話するたびに、新しい接続オブジェクトを作成する意味がわかりません。もちろん、一度に別のデータベースに接続する必要がある場合は、別の接続オブジェクトを作成する必要があります。
RWendi 2008

0

とても簡単です。グローバルORシングルトンは絶対に使用しないでください。


4
それはSingletonパターンになると、常に決して言わない
1800 INFORMATION

3
複数のログプロバイダーが必要な場合はどうなりますか?ファイルとコンソールにログインできないと誰が言いますか?
1800 INFORMATION

2
あなたは、別の(神のオブジェクト)を1アンチパターン(シングルトン)を組み合わせますので
1800 INFORMATION

3
はい。そして、グローバルクラスオブジェクトを許可することにより、すべての主要なOOP言語の設計者もそうします。あなたが一神教徒であり、オブジェクトが神を表すことを望むなら、あなたが探しているのはシングルトン神オブジェクトです。それ以外は正しくありません。
スティーブジェソップ

4
私が月刊誌であり、神オブジェクトを作成したい場合、MonotheistAbstractFactoryパターンを使用して神オブジェクトを作成するように指定できますか?これにより、多神教のユーザーがPolytheistAbstractfactoryを指定して、私のプログラムを使用する可能性が開かれます。
1800 INFORMATION

0

アドバイスとして、シングルトングローバルの両方が有効であり、同じシステム、プロジェクト、プラグイン、製品などに参加できます...私の場合、私はWeb(プラグイン)用のデジタル製品を作成しています。

メインクラスではシングルトンのみを使用しており、原則として使用しています。メインクラスが再びインスタンス化しないことを知っているので、私はほとんどそれを使用しません

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

ほぼすべてのセカンダリクラスのグローバルな使用例:

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

メインの製品クラスの別のインスタンスは必要ないため、実行時にGlobalを使用して同じインスタンスでメソッドと属性を呼び出すことができます。

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

同じクラスのインスタンス用のファクトリは必要ないため、グローバルでは同じインスタンスを使用して製品を機能させることができます。通常、インスタンスファクトリは大規模なシステム用または非常にまれな目的です。

In conclusion:、アンチパターンシングルトンをすでによく理解していてグローバルを理解している場合は、2つのオプションのいずれかを使用するか、それらを組み合わせることができますが、非常に例外的で忠実なプログラマーがたくさんいるため、乱用しないことをお勧めしますプログラミングOOPは、実行時間内に頻繁に使用するメインクラスとセカンダリクラスに使用します。(それはあなたに多くのCPUを節約します)。😉

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.