ホームページ >バックエンド開発 >PHPチュートリアル >PHP デザイン パターンの概要 IV シングルトン パターン_PHP チュートリアル
ほとんどすべてのオブジェクト指向プログラムでは、常に 1 つまたは 2 つのリソースが作成され、プログラム アプリケーション内で共有され、使用され続けます。たとえば、このようなリソースは電子商取引プログラムのデータベース接続で使用されます。この接続はアプリケーションの開始時に初期化され、プログラムが終了すると効果的に実行できるようになりますが、最終的に接続は切断されて破棄されます。コードを記述すると、毎回データベース接続を作成する必要がなくなりますが、これは非常に非効率です。確立された接続は、コードで簡単に再利用できる必要があります。問題は、上記の要件に基づいてこのデータベース接続をどのように行うかです (または、開いているファイルやキューなど、リサイクルされる他の固有のリソースに接続します)。
質問特定のクラスのインスタンスが一意であること (このクラスの唯一のインスタンスであること)、および簡単にアクセスできることをどのように確認しますか?
解決策
もちろん、グローバル変数は明白な解決策です。しかし、それはパンドラの箱 (正しい判断は経験から生まれ、間違った判断は経験から生まれます。それがことわざの意味です。) のようなもので、コードのいずれかがグローバル変数を変更する可能性があり、必然的により多くのデバッグ事故を引き起こすことになります。言い換えれば、グローバル変数の状態には常に何らかの問題が存在します (グローバル変数の使用に関する問題については、http://c2.com/cgi/wiki?GlobalVariablesAreBad で詳しく説明されています)。
特別なクラスの一意のインスタンスが必要な場合は、シングルトンと呼ばれるこのパターンを使用します。シングルトン パターンに基づくクラスは、このクラスのインスタンスをインスタンス化して初期化し、毎回まったく同じ接続を提供できます。通常、これは getInstance() という名前の静的メソッドを使用して実装されます。
重要な問題は、どのようにして正確で統一されたインスタンスを常に取得するかということです。以下の例をご覧ください:
// PHP4
関数 TestGetInstance() {
$this->assertIsA(
$obj1 =& DbConn::getInstance(),
「DbConn」、
「返されたオブジェクトは DbConn のインスタンスです」);
$this->assertReference(
$obj1,
$obj2 =& DbConn::getInstance(),
「getInstance() を 2 回呼び出すと同じオブジェクトが返されます」);
}
コメント:assertReference
assertReference() メソッドは、渡された 2 つのパラメータが同じ PHP 変数を参照することを保証します。
PHP4では、テストされる2つのパラメータは同じオブジェクトであると主張されます。 assertReference() メソッドは、PHP5 に移植された後は推奨されない可能性があります。
このテスト メソッドには 2 つのアサーションがあります。1 つ目は静的メソッド DbConn::getInstance() によって返される値が DbConn オブジェクトのインスタンスであることを決定し、2 つ目は 2 番目の呼び出しによって返される値参照を決定するために使用されます。 getInstance() メソッドに対するものは同じオブジェクト インスタンスです。つまり、同じオブジェクトを使用します。
コードの予想される実行結果をアサートすることに加えて、Test は getInstance() (PHP4) の正しい使用法も示します: $local_conn_var=&DbConn::getInstance()。参照 (=&) 静的メソッドの戻り値がこのローカル変数に代入されます。
別のテスト コードを作成します。「new」を直接使用してシングルトン クラスをインスタンス化すると、特定の種類のエラーが発生します。テストコードは次のとおりです:
関数 TestBadInstantiate() {
$obj =& 新しい DbConn;
$this->assertErrorPattern(
「/(悪い|嫌な|悪|しない|しない|警告).*」。
‘(インスタンス|作成|新規|ダイレクト)/i');
}
このコードは DbConn のインスタンスを直接作成するため、PHP がエラーを報告します。コードをより安定させるために、PCRE 正規表現を使用してエラー メッセージを照合します。 (表示されるエラーメッセージの正確な文言は重要ではありません。)
[次へ]
サンプルコード
シングルトンモードは非常に興味深いモードです。ここでは PHP4 から始めて、PHP4 と PHP5 を使用してその実装プロセスを見てみましょう。
グローバルなアプローチ
理論的には、グローバル変数は完全なシングルトンを生成できますが、グローバル変数は変更される可能性があります。コードの実行中に、グローバル変数がオブジェクトを指すという保証はありません。したがって、グローバル変数をグローバルに直接参照できないようにすることで、グローバル変数への「アクセスがランダムすぎる」という問題を軽減できます。たとえば、このコードは、非常に長くて一意の名前を使用して、グローバル変数への参照を「隠し」ます。
クラス DbConn {
関数 DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error('DbConn クラスはシングルトンです'
.'直接インスタンス化しないでください。');
}
}
関数&getInstance() {
$key = ‘__some_unique_key_for_the_DbConn_instance__’;
if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key])
&& ‘dbconn’ == get_class($GLOBALS[$key]) )) {
$GLOBALS[$key] =& new DbConn(M_E);
}
$GLOBALS[$key]を返す;
}
}
DbConn のコンストラクターでは、$fromGetInstance のデフォルトのパラメーターについて混乱する可能性があります。オブジェクトが直接インスタンス化される場合、(非常に弱い) 保護を提供します。デフォルト値が e (PHP の数学定数の M_E = 2.718281828459) にならない限り、このコードはエラーを報告します。
UML クラス図で表すと、解決策は次のようになります。
この「謎のパラメータ」 - タイプ保護を選択しない場合は、グローバル タグを作成することもオプションであり、それを使用して getInstance() メソッドを通じてオブジェクトを作成したことを確認します。保全のアプローチは、「名前は知っている」から「環境の中にある」へと変わります。
コンストラクター保護コードにグローバル フラグがある理由を説明する例を次に示します。
クラス DbConn {
関数 DbConn() {
$token = ‘__some_DbConn_instance_create_semaphore__’;
if (!array_key_exists($token, $GLOBALS)) {
trigger_error('DbConn クラスはシングルトンです'
.'直接インスタンス化しないでください。');
}
}
関数&getInstance() {
static $instance = array();
if (!$instance) {
$token = ‘__some_DbConn_instance_create_semaphore__’;
$GLOBALS[$token] = true;
$instance[0] =& 新しい DbConn;
unset($GLOBALS[$token]);
}
ヒント
PHP4ではコンストラクター内で$thisの値を変更できます。以前は、コンストラクター作成エラーが発生した場合、無効なオブジェクトがコードで使用できないように $this = null; を設定していました。 PHP4 では便利だったものは PHP5 では互換性がないため、将来この手法は推奨されなくなります。
このコードのもう 1 つの重要なポイントは、参照演算 & の使用です。 & を使用する必要がある場所が 2 か所あります。最初のものは、関数を定義するときに関数名の前に使用され、参照が返されることを示します。 2 つ目は、新しい DbConn オブジェクトを $GLOBALS 配列に割り当てることです。 (序文と値オブジェクトの章で説明されています: PHP4 では、参照によってオブジェクトを作成、渡し、返すには常に & 演算子を使用します)
getInstance() メソッドの条件チェックは、エラー レベルが E_ALL であっても、警告なしで実行されるように書かれていることがよくあります。 $GLOBAL 配列内の適切な場所に DbConn オブジェクトがあるかどうかを確認し、存在しない場合はそこにオブジェクトを作成します。このメソッドは、オブジェクトが繰り返し作成できるか、またはオブジェクトが以前にこのメソッドによって作成されたことがあるという結果を返します。メソッドが終了すると、クラスの有効なインスタンスが存在し、それが効果的に初期化されたことを確認できます。
[次へ]
静的メソッド
グローバル変数の問題については、getInstance()に隠されたグローバル変数にも存在します。グローバル変数はスクリプト内のどこでも有効であるため、気付かずにこのグローバル変数を破棄する可能性があります
静的変数を使用して getInstance() メソッド内にシングルトンを格納するのはクリーンなアプローチです。最初のコード スニペットは次のとおりです:
クラス DbConn {
// ...
関数&getInstance() {
static $instance = false;
if (!$instance) $instance =& new DbConn(M_E);
$instance を返す;
}
}
Zend 1 エンジンは、PHP4 の静的変数への参照を保存できません (http://www.php.net/manual/en/ language.variables.scope.php#AEN3609 を参照)。ワークスペースを使用して静的配列を保存し、シングルトン インスタンスへの参照を既知の配列に配置します。 getInstance() メソッドは次のとおりです:
クラス DbConn {
関数 DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error('DbConn クラスはシングルトンです'
.'直接インスタンス化しないでください。');
}
}
関数&getInstance() {
static $instance = array();
if (!$instance) $instance0 =& new DbConn(M_E);
$instance0 を返す;
}
}
このコードは、静的配列 $instancede の最初の要素を選択して、単一の DbConns インスタンスへの参照を保持するだけです。
このコードは PHP の Boolean メソッドに少し依存していますが、グローバル バージョンよりも厳密です。条件をテストするときに空の配列を使用すると false になります。以前のバージョンの DbConn クラスと同様に、関数の定義部分と代入部分には参照演算子が必要です。
PHP5のシングルモード
PHP5ではシングルトンモードの実装が容易になり、PHP5ではクラスの内部変数や関数へのアクセス制御が強化されました。 DbConn::_construct() コンストラクターをプライベートに設定すると、このクラスを直接インスタンス化できなくなります。 UML 図で表すと、PHP5 の DbConn シングルトン モードは次のとおりです:
静的メソッドと静的変数を組み合わせてこのインスタンスを維持し、クラスの直接インスタンス化によるインスタンスの作成を防ぐためにコンストラクターをプライベートに設定します。 コードは次のとおりです。
クラス DbConn {/**
* シングルトンインスタンスを保持する静的プロパティ
*/
static $instance = false;
/**
*コンストラクター
*プライベートなのでgetInstance()メソッドのみインスタンス化できます
* @return void
*/
プライベート関数 __construct() {}
/**
* シングルトンインスタンスを返すファクトリメソッド
* @return DbConn
*/
パブリック関数 getInstance() {
if (!DbConn::$instance) {
DbConn::$instance = 新しい DbConn;
}
DbConn::$instance を返す;
}
}へ
http://www.bkjia.com/PHPjc/767405.htmlwww.bkjia.com
truehttp://www.bkjia.com/PHPjc/767405.html