PHPによるデザインパターン1 Singleton
会社でデザインパターン勉強会を始めたので、その内容を書き残していきたいと思います。
23回に分けて全パターンを理解するのが目標。
生成するオブジェクトの数を1つに制限する為のパターン。
(GoFパターンの中で唯一、ひとつのクラスで完結するパターンとなる)
23回に分けて全パターンを理解するのが目標。
Singletonパターン
【目的】生成するオブジェクトの数を1つに制限する為のパターン。
(GoFパターンの中で唯一、ひとつのクラスで完結するパターンとなる)
Q. なぜ制限する必要があるのか?
A1. クラスのインスタンスはnew演算子で生成されます。
5回newすれば5つのインスタンスが生成されます。
しかしインスタンスを生成するのはコストがかかるので、使い回した方が効率が良い場面があります。
A2. 「システム全体で読み込んだデータをキャッシュしておくクラス」等、どうしてもインスタンスを1つしか作りたくない場面があります。
【具体的な実装内容】
インスタンスへのアクセスを制御する
【コード例】
<?php class SingletonSample { private $_id; private static $_instance; /** * コンストラクタ */ private function __construct() { $this->_id = md5(date('ymdhis') . mt_rand()); } /** * クラスを生成する為の唯一の窓口 */ public static function getInstance() { if (!isset(self::$_instance)) { self::$_instance = new SingletonSample; } return self::$_instance; } /** * IDを返す */ public function getId() { return $this->_id; } /** * 複製を禁止する */ public final function __clone() { throw new RuntimeException('Singletonパターンの為、cloneキーワードの使用は禁止されています。'); } }
【呼び出し側のコード例】
<?php // sample1 $instance1 = SingletonSample::getInstance(); $instance2 = SingletonSample::getInstance(); echo $instance1 === $instance2; // true が返る echo $instance1->getId(); echo $instance2->getId(); // 上と同じidが返る // sample2 $instance = new SingletonSample; // Fatal Error が発生する // sample3 $instance = SingletonSample::getInstance(); $instance_clone = clone $instance; // RuntimeException が throw され、Fatal Error が発生する
【clone キーワードについて】
PHP4では、オブジェクトのコピーは値渡しになっていましたが、PHP5では参照渡しに変更されています。
ex) $copy_obj = $obj; // PHP4では値渡し。PHP5では参照渡し。
なので、PHP5でオブジェクトの値渡しをする際は、cloneキーワードが用意されています。
ex) $copy_obj = clone $obj; // 値渡しになる。
なお、このcloneキーワードの挙動はマジックメソッドで上書きできる為、クラス内でオーバーライド可能です。
【もう一つの実装コード例】
<?php /** * クラスを生成する為の唯一の窓口 */ public static function getInstance() { /** クラスの静的変数として持つか、メソッド内の静的変数として持つかだけの違い */ static $instance; if (!isset($instance)) { $instance = new SingletonSample; } return $instance; }
【ZendFramework における Singletonパターンの例】
ZF 1.9.6 Zend_Controller_Front クラスより抜粋
<?php /** * Constructor * * Instantiate using {@link getInstance()}; front controller is a singleton * object. * * Instantiates the plugin broker. * * @return void */ protected function __construct() { $this->_plugins = new Zend_Controller_Plugin_Broker(); } /** * Enforce singleton; disallow cloning * * @return void */ private function __clone() { } /** * Singleton instance * * @return Zend_Controller_Front */ public static function getInstance() { if (null === self::$_instance) { self::$_instance = new self(); } return self::$_instance; }
【PHP4 での実装例】
<?php class SingletonSample { /** * 呼び出し側はこのメソッドを使う!new は使っちゃダメ */ function &getInstance() { /** */ static $singleton; if (!isset($singleton)) { $singleton = new Singleton(); } return $singleton; } }
【呼び出し側のコード例】
<?php $instance1 =& SingletonSample::getInstance(); $instance2 =& SingletonSample::getInstance(); echo $instance1 === $instance2; // true が返る $instance = new SingletonSample; // これを制限する手段は言語仕様上できない為、運用ルールで制限するしかない