web-dev-qa-db-ja.com

PHPでデータベースにアクセスするシングルトンのユースケースはありますか?

PDO経由でMySQLデータベースにアクセスします。データベースへのアクセスを設定していますが、最初の試みは以下を使用することでした:

私が最初に考えたのはglobal

$db = new PDO('mysql:Host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

これは悪い習慣と見なされます。少し検索した結果、 シングルトンパターン になりました。

「クラスの単一インスタンスが必要な状況に適用されます。」

マニュアルの例によると、これを行う必要があります。

class Database {
    private static $instance, $db;

    private function __construct(){}

    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;

        return self:$instance;
    }

    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:Host=127.0.0.1;dbname=toto', 'user', 'pwd')

        return self::$db;
    }
}

function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}

some_function();

これができるのに、なぜ比較的大きなクラスが必要なのですか?

class Database {
    private static $db;

    private function __construct(){}

    static function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:Host=127.0.0.1;dbname=toto', 'user', 'pwd');

        return self::$db;
    }
}

function some_function() {
    Database::get()->query('...');
}

some_function();

この最後のものは完璧に機能し、$dbを心配する必要はもうありません。

より小さいシングルトンクラスを作成するにはどうすればよいですか、またはPHPにないシングルトンのユースケースはありますか?

136
seriousdev

さて、最初にキャリアを始めたとき、私はしばらくそのことを考えました。さまざまな方法で実装し、静的クラスを使用しないことを選択する2つの理由を考え出しましたが、それらは非常に大きなものです。

1つは、非常に頻繁に、複数のインスタンスが存在することは絶対にないと確信し、最終的には2番目のインスタンスが存在することを発見することです。最終的には、2番目のモニター、2番目のデータベース、2番目のサーバーのいずれでも構いません。

これが発生すると、静的クラスを使用した場合は、シングルトンを使用した場合よりもはるかに悪いリファクタリングを行うことになります。シングルトンはそれ自体が不明瞭なパターンですが、かなり簡単にインテリジェントなファクトリパターンに変換されます。あまりにも問題なく依存性注入を使用するように変換することもできます。たとえば、シングルトンがgetInstance()を介して取得される場合、それをgetInstance(databaseName)に非常に簡単に変更して、複数のデータベースを許可できます。他のコードは変更できません。

2番目の問題はテストです(正直なところ、これは最初の問題と同じです)。データベースを模擬データベースに置き換えたい場合があります。実際には、これはデータベースオブジェクトの2番目のインスタンスです。これは、シングルトンを使用する場合よりも静的クラスを使用する方がはるかに困難です。静的クラスのすべてのメソッド(一部の言語では非常に困難な場合があります)ではなく、getInstance()メソッドをモックアウトするだけです。

それは本当に習慣に帰着します-そして、人々が「グローバル」が悪いと言うとき、彼らはそう言う非常に正当な理由を持っています、しかしあなたがあなた自身で問題にぶつかるまで、それは常に明白ではないかもしれません。

あなたができる最善のことは、(あなたがしたように)尋ねてから選択を行い、あなたの決定の結果を観察することです。時間の経過とともにコードの進化を解釈するための知識を持つことは、そもそもコードを進化させるよりもはるかに重要です。

79
Bill K

シングルトンには、PHPでの使用はほとんどありません(そうでないとしても)。

オブジェクトが共有メモリに存在する言語では、シングルトンを使用してメモリ使用量を低く抑えることができます。 2つのオブジェクトを作成する代わりに、グローバルに共有されたアプリケーションメモリから既存のインスタンスを参照します。 In PHPそのようなアプリケーションメモリはありません。1つのリクエストで作成されたシングルトンは、そのリクエストに対して存続します。同時に行われた別のリクエストで作成されたシングルトンは、シングルトンの2つの主な目的の1つはここでは適用されません。

さらに、アプリケーション内に概念的に一度しか存在できないオブジェクトの多くは、これを強制するための言語メカニズムを必ずしも必要としません。 1つのインスタンスのみneedを使用する場合、 別のインスタンスを作成しないでください 。あなたが他のインスタンスを持たない場合のみです。 2番目のインスタンスを作成するときに子猫が死んだとき、シングルトンの有効なユースケースがあるかもしれません。

もう1つの目的は、同じリクエスト内のインスタンスへのグローバルアクセスポイントを持つことです。これは望ましいように思えるかもしれませんが、グローバルスコープ(グローバルや静的など)へのカップリングを作成するため、実際にはそうではありません。 これにより単体テストが難しくなります であり、一般的にアプリケーションの保守性が低下します。これを軽減する方法はありますが、一般に、多くのクラスで同じインスタンスを使用する必要がある場合は、 Dependency Injection を使用します。

Singletons in PHP-なぜ悪いのか、アプリケーションからそれらを削除する方法 のスライドをご覧ください。

Erich Gamma (シングルトンパターンの発明者の1人)は、最近このパターンを疑っています:

「シングルトンをドロップすることに賛成です。その使用はほとんど常にデザインの匂いです」

参考文献

上記の後に、まだ決定するのに助けが必要な場合:

Singleton Decision Diagram

317
Gordon

PHPでシングルトンが必要なのは誰ですか?

シングルトンに対する異議のほとんどすべてが技術的な観点から来ていることに注意してください-しかし、それらはその範囲も非常に制限されています。特にPHPの場合。最初に、シングルトンを使用する理由のいくつかをリストし、次にシングルトンの使用に対する異議を分析します。まず、それらを必要とする人々:

-多くの異なる環境で使用される大規模なフレームワーク/コードベースをコーディングしている人は、以前に存在していたさまざまなフレームワーク/コードベースで作業する必要があります。クライアント、上司、経営陣、ユニットリーダーからの気まぐれなリクエストでも変わります。

参照してください、シングルトンパターンは自己包括的です。完了すると、シングルトンクラスは、それを組み込むコード全体で厳格になり、メソッドや変数を作成した方法とまったく同じように機能します。そして、それは与えられたリクエストでは常に同じオブジェクトです。 2つの異なるオブジェクトになるように2回作成することはできないため、シングルトンオブジェクトがコードの任意のポイントにあることを知っています-シングルトンが2つ、3つの異なる、さらにはスパゲッティコードベースに挿入される場合でも。したがって、開発目的の面で簡単になります-そのプロジェクトで作業する人が多い場合でも、特定のコードベースのある点でシングルトンが初期化されるのを見ると、それが何で、何をし、どのように従来のクラスである場合、そのオブジェクトが最初に作成された場所、コード内のそのポイントまでに呼び出されたメソッド、およびその特定の状態を追跡する必要があります。しかし、そこにシングルトンをドロップし、適切なデバッグおよび情報メソッドをドロップし、コーディング中にシングルトンにトラッキングすると、それが何であるかを正確に知ることができます。そのため、異なるコードベースで作業しなければならない人々にとって、それは異なる哲学で以前に行われたコード、またはあなたが接触していない人々によって行われたコードを統合する必要性を容易にします。 (つまり、vendor-project-company-whateverはもう存在せず、何もサポートしません)。

-サードパーティ API 、サービス、およびWebサイトで作業する必要がある人々

よく見ると、これは以前の場合とそれほど違いはありません-サードパーティのAPI、サービス、ウェブサイトは、制御できない外部の分離されたコードベースのようです。何でも起れる。そのため、シングルトンセッション/ユーザークラスを使用すると、 OpenIDFacebookTwitter など-そして、これらすべてを同じシングルトンオブジェクトから同時に実行できます。これは、任意の時点で既知の状態で簡単にアクセスできます。あなたがそれをプラグインするどんなコードでも。独自のWebサイト/アプリケーションで、同じユーザーの複数の異なるサードパーティAPI /サービスへの複数のセッションを作成し、それらを使用して何でもできます。

もちろん、これはすべて、通常のクラスとオブジェクトを使用することにより、従来の方法で調子を整えることができます-ここでのキャッチは、シングルトンが整頓されていてきれいです。

-迅速な開発を行う必要がある人々

シングルトンのグローバルな動作により、構築するシングルトンのコレクションを含むフレームワークであらゆる種類のコードを簡単に構築できます。シングルトンクラスを適切に構築すると、確立された成熟したsetメソッドが簡単に利用できるためです。いつでもどこでも一貫した方法で使用できます。クラスを成熟させるには時間がかかりますが、その後は堅実で一貫性があり、便利です。シングルトンで必要なだけメソッドを実行できます。これにより、オブジェクトのメモリフットプリントが増加する可能性がありますが、迅速な開発に必要な時間を大幅に節約できます。アプリケーションは別の統合されたアプリケーションで使用でき、クライアント/ボス/プロジェクトマネージャーが要求する新しい機能をたった数回変更するだけで使用できます。

アイデアを得た。次に、シングルトンへの異議と、有用なものに対する不浄な十字軍に移りましょう。

-何よりも異論は、テストが難しくなることです。

そして実際には、適切な予防措置を講じ、シングルトンをデバッグすることに気付いてデバッグルーチンをシングルトンにコーディングすることで簡単に軽減できる場合でも、ある程度は実行されます。しかし、これは世の中にある他のコーディング哲学/方法/パターンとあまり変わらない-ただ、シングルトンは比較的新しく、普及していないので、現在のテスト方法は最終的にそれらと互換性がないことになります。しかし、それはプログラミング言語のどの面でも違いはありません。スタイルが異なると異なるアプローチが必要になります。

この異論は、アプリケーション開発の理由が「テスト」のためではなく、アプリケーション開発に移行する唯一のフェーズ/プロセスではないという事実を無視しているという点で、この異論の1つです。アプリケーションは実稼働用に開発されています。また、「シングルトンが必要なユーザー」セクションで説明したように、シングルトンは、多くの異なるコードベース/アプリケーション/サードパーティサービスでコードを使用および内部で動作させる必要があるという複雑さから大きな取り引きを削減できます。テストで失われる可能性のある時間は、開発と展開で得られる時間です。これは、サードパーティの認証/アプリケーション/統合の時代、特にFacebook、Twitter、OpenIDなど、多くの人、そして次のことを知っている人にとって特に便利です。

それは理解できますが、プログラマーはキャリアによって非常に異なる状況で働いています。そして、異なる大規模な企業で働いている人々のために、異なる定義されたソフトウェア/アプリケーションを快適な方法で、予算削減/レイオフの差し迫った運命とそれに伴う多くの異なるもので多くのものを行う必要性なしで安価/高速/信頼できるファッション、シングルトンはそれほど必要ではないように見えるかもしれません。そして、それは彼らがすでに持っているものへの迷惑/障害でさえあるかもしれません。

しかし、「アジャイル」開発の汚い溝で作業する必要があり、クライアント/マネージャー/プロジェクトから多くの異なる要求(時には不合理な)を実装しなければならない人にとって、シングルトンは前述の理由により恵まれています。

-もう1つの異論は、メモリフットプリントが高いことです

各クライアントからのリクエストごとに新しいシングルトンが存在するため、これはPHPの反対になる可能性があります。正しく構成されていないシングルトンを使用すると、特定の時点で多くのユーザーがアプリケーションによって処理される場合、アプリケーションのメモリフットプリントが大きくなる可能性があります。

ただし、これは、コーディング中に使用できるあらゆる種類のアプローチに有効です。尋ねるべき質問は、これらのシングルトンが保持および処理する方法、データは不要ですか?なぜなら、アプリケーションが取得するリクエストの多くでそれらが必要な場合、シングルトンを使用しなくても、それらのメソッドとデータは何らかの形でコードを通じてアプリケーションに存在するからです。したがって、従来のクラスオブジェクトをコード処理に1/3で初期化し、3/4に破棄すると、すべてのメモリを節約するかどうかの問題になります。

この方法を使用すると、シングルトンを使用するかどうかに関係なく、不要なメソッド、コード内のオブジェクトに保持されているデータはありません-質問はまったく無関係になります。したがって、シングルトンに対するこの異議は、あなたが使用するクラスから作成されたオブジェクトに不要なメソッド、データがあると仮定するという点で非常に面白くなります。

-「複数のデータベース接続の維持を不可能/困難にする」などのいくつかの無効な反対意見

特定のシングルトンで複数のデータベース接続、複数のデータベース選択、複数のデータベースクエリ、複数の結果セットを維持する必要がある場合、シングルトンの変数/配列でそれらを保持している限り、この異議を理解することさえできませんそれらが必要です。これは、それらを配列に保持するのと同じくらい簡単にできますが、それを実現するために使用する任意の方法を考案できます。しかし、特定のシングルトンでの変数と配列の使用という最も単純なケースを調べてみましょう。

以下が特定のデータベースシングルトン内にあると想像してください。

$ this-> connections =array(); (間違った構文、画像を表示するためにこのように入力しました-変数の適切な宣言はpublic $ connections = array();そしてその使用法は自然に$ this-> connections ['connectionkey']です)

この方法で、アレイ内の任意の時点で複数の接続をセットアップし、維持することができます。クエリ、結果セットなども同様です。

$ this-> query(QUERYSTRING、 'queryname'、$ this-> connections ['particulrconnection']);

選択された接続で選択されたデータベースにクエリを実行し、

$ this-> results

キー 'queryname'を持つ配列。もちろん、このためにクエリメソッドをコーディングする必要があります-これは簡単です。

これにより、必要に応じて、事実上無限の数(もちろん、リソースの制限が許す限り)の異なるデータベース接続と結果セットを維持できます。そして、このシングルトンクラスがインスタンス化されたコードベースの特定のポイントの任意のコードで使用できます。

もちろん、結果セットと接続は不要なときに解放する必要がありますが、言うまでもなく、シングルトンやその他のコーディング方法/スタイル/コンセプトに固有のものではありません。

この時点で、同じシングルトンでサードパーティのアプリケーションまたはサービスへの複数の接続/状態を維持する方法を確認できます。それほど違いはありません。

端的に言えば、結局のところ、シングルトンパターンは、プログラムするための別の方法/スタイル/哲学であり、正しい場所で正しい方法で使用する場合、他のパターンと同じくらい有用です。これは何も違いはありません。

シングルトンがバッシュされているほとんどの記事では、「グローバル」が「悪」であるという言及も見られます。

それに直面しましょう-適切に使用されていない、虐待されている、誤用されている、IS悪。それは、言語、コーディング概念、方法に限定されません。 「X is evil」、その記事から逃げる。それは限られた視点の産物である可能性が非常に高い。与えられたスタイル/方法にほとんど従う-典型的な知的保守主義。

そのために、「グローバルは悪」から「iframeは悪」まで、無限の例を挙げることができます。約10年前、特定のアプリケーションでiframeの使用を提案することさえ異端でした。その後、Facebookが登場し、あらゆる場所でiframeが実行され、何が起こったのかがわかります。iframeはもはやそれほど悪ではありません。

まだ頑固に「悪」であると主張する人々がいます-そして、時には正当な理由もあります-しかし、ご覧のように、iframeがそのニーズを満たし、うまく機能しているため、世界全体が動き続けています。

プログラマー/コーダー/ソフトウェアエンジニアの第一の資産は、自由で開かれた柔軟な心です。

21
unity100

シングルトンは多くの人が anti-patterns であると見なされています。これは、実際には単なるグローバル変数の栄光に過ぎないからです。実際には、クラスにインスタンスが1つだけである必要がある必要なシナリオは比較的少数です。通常、1つのインスタンスがsufficientであるだけです。この場合、シングルトンとして実装することは完全に不要です。

質問に答えるために、あなたはシングルトンがここで過剰であることは正しいです。単純な変数または関数で十分です。ただし、より優れた(より堅牢な)アプローチは、 dependency injection を使用して、グローバル変数の必要性を完全に削除することです。

15
Will Vousden

あなたの例では、一見変化しない一片の情報を扱っています。この例では、シングルトンは過剰であり、クラスで静的関数を使用するだけで十分です。

より多くの考え:パターンのためにパターンを実装するケースを経験しているかもしれず、あなたの腸はあなたが綴った理由のために「いいえ、あなたはする必要はない」と言っています。

ただし:プロジェクトのサイズと範囲についてはわかりません。これが単純なコードである場合、おそらく捨ててください。変更する必要はほとんどないので、はい、先に進み、静的メンバーを使用します。ただし、プロジェクトのスケーリングが必要な場合、またはメンテナンスコーディングのために準備する必要がある場合は、はい、シングルトンパターンを使用することをお勧めします。

8
Paul Sasik

最初に、Singletonパターンの用途はあまりないと言いたいだけです。なぜアプリケーション全体で単一のオブジェクトを保持したいのですか?特にデータベースの場合、別のデータベースサーバーに接続したい場合はどうすればよいですか?毎回切断して再接続する必要があります...?とにかく...

アプリケーションでグローバルを使用することには、いくつかの欠点があります(これは、Singletonパターンの従来の使用方法です)。

  • 単体テストが難しい
  • 依存性注入の問題
  • ロックの問題を作成できる(マルチスレッドアプリケーション)

シングルトンの最大の問題は静的getInstanceメソッドであるため、シングルトンインスタンスの代わりに静的クラスを使用すると、同じ欠点がいくつかあります。

従来のgetInstanceメソッドを使用せずに、クラスが持つことができるインスタンスの数を制限できます。

class Single {

    static private $_instance = false;

    public function __construct() {
        if (self::$_instance)
           throw new RuntimeException('An instance of '.__CLASS__.' already exists');

        self::$_instance = true;
    }

    private function __clone() {
        throw new RuntimeException('Cannot clone a singleton class');
    }

    public function __destruct() {
        self::$_instance = false;
    }

}

$a = new Single;
$b = new Single; // error
$b = clone($a); // error
unset($a);
$b = new Single; // works

これは、上記の最初のポイントに役立ちます:単体テストと依存関係の注入。クラスの単一のインスタンスがアプリケーションに存在することを確認しながら。たとえば、結果のオブジェクトをモデル(MVCパターン)に渡すだけで使用できます。

5
netcoder

ソリューションがPHPのドキュメントに記載されているものとどのように異なるかを考えてみてください。実際、1つの「小さな」違いがあります。ソリューションはゲッターの呼び出し元にPDOインスタンス、ドキュメントの1つはDatabase::singletonDatabaseインスタンスで使用します(その後、ゲッターを使用してPDOインスタンスを取得します)。

それでは、どのような結論に達しますか?

  • ドキュメントコードでは、呼び出し元はDatabaseインスタンスを取得します。 Databaseクラスは、実際には(この(すべての問題に対処する場合は)shouldを公開します)ラップするPDOオブジェクトよりもレベルのインターフェイス。
  • 実装を変更してPDO以外の(より豊富な)型を返す場合、2つの実装は同等です。手動の実装に従うことで得られる利益はありません。

実用的な面では、シングルトンはかなり物議を醸すパターンです。これは主に次の理由によります。

  • 使いすぎです。初心者プログラマーは、他のパターンを理解するよりもはるかに簡単にシングルトンを理解します。シングルトンがなくても手元の問題をより良く解決できる場合でも、彼らは新しい発見した知識をあらゆる場所に適用し続けます(ハンマーを持っているときは、すべてが釘のように見えます)。
  • プログラミング言語に応じて、気密で漏れのない方法でシングルトンを実装することは、タイタニックなタスクであることがわかります(特に、高度なシナリオがある場合:シングルトンに依存するシングルトン、破棄および再作成できるシングルトンなど) )。 C++での「決定的な」シングルトン実装を検索してみてください(私はAndrei Alexandrescuの画期的なModern C++ Designを所有しています)。
  • シングルトンをコーディングするときと、それにアクセスするためのコードを書くときの両方で、ワークロードを追加します。これは、プログラム変数を使用して何をしようとするかについてのいくつかの自主的な制約に従うことなく実行できます。

つまり、最終的な結論として:あなたのシングルトンは問題ありません。ほとんどの場合、シングルトンをまったく使用しなくても問題ありません。

5
Jon

私はこれについて何の意味も見ていません。接続文字列がコンストラクターのパラメーターとして使用され、 [〜#〜] pdo [〜#〜] オブジェクトのリストを保持するような方法でクラスを実装した場合接続文字列)があれば、いくつかの利点があるかもしれませんが、この場合のシングルトンの実装は無意味な練習のようです。

2
Kell

あなたの解釈は正しいです。シングルトンには場所がありますが、使いすぎです。多くの場合、静的メンバー関数にアクセスするだけで十分です(特に、何らかの方法で構築時間を制御する必要がない場合)。さらに、いくつかの無料の関数と変数を名前空間に置くことができます。

プログラミングするとき、「正しい」と「間違った」はありません。 「良い習慣」と「悪い習慣」があります。

シングルトンは通常、後で再利用されるクラスとして作成されます。プログラマーが、真夜中に酔っ払ってコーディングしているときに誤って2つのインスタンスをインスタンス化しないように作成する必要があります。

を2回以上インスタンス化すべきではない単純な小さなクラスがある場合、needそれをシングルトンにします。そうした場合、それは単なる安全策です。

alwaysではなく、グローバルオブジェクトを持つのは悪い習慣です。グローバル/どこでも/常に使用することがわかっている場合は、いくつかの例外の1つである可能性があります。ただし、グローバルはgotoが悪い習慣と見なされるのと同じように、一般的に「悪い習慣」と見なされます。

2
zzzzBov

私が見る限り、あなたは何も欠けていません。この例にはかなり欠陥があります。シングルトンクラスに非静的インスタンス変数がある場合、違いが生じます。

1
FooLman