singletonって何ぞや
-
たくさんある「デザインパターン」のうちの一つみたいです。
「オブジェクト指向における再利用のためのデザインパターン」(GoF本)で紹介されています。すごく偉い4人のプログラマーが、「これはわりと使える場面あるやろ」と言う感じでまとめてくれたものですね。 -
singletonを使うとそのクラスのインスタンスが絶対に1つである事を保証してくれます。 アプリケーションの全てのリクエストを通して常に一つである場合などが使いどころみたいです。
PHP the right wayにおけるsingleton
出典: PHP the right way - デザインパターン
getInstanceメソッドを呼ぶことでSingletonクラスがインスタンス化されますが、その後呼び出しても同一のインスタンスが返ってくるようになります。つまり一つしか作れないわけです。
<?php
class Singleton
{
/**
* Returns the *Singleton* instance of this class.
*
* @staticvar Singleton $instance The *Singleton* instances of this class.
*
* @return Singleton The *Singleton* instance.
*/
public static function getInstance()
{
static $instance = null;
if (null === $instance) {
$instance = new static();
}
return $instance;
}
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct()
{
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* @return void
*/
private function __clone()
{
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* @return void
*/
private function __wakeup()
{
}
}
あたりまえですが
$obj = Singleton::getInstance;
var_dump($obj === Singleton::getInstance());
はtrueです。
実際、どういう使われ方してるのか
Yiiの場合だとCWebApplication(コンソール使うとCConsoleApplicationになるかも)という基底クラスを1度しかインスタンス化できないように使われています。
エントリスクリプトにて
// アプリケーションインスタンスを生成して実行します。
$configFile='path/to/config/file.php';
Yii::createWebApplication($configFile)->run();
Yiiクラス(実際は継承しているYiiBase)のcreateWebApplicationによってCWebApplicationクラスがインスタンス化
↓
CWebApplicationが継承しているabstractクラスであるCApplicationのコンストラクタにYii::setApplicationがあり、一度だけインスタンスを作成するようになっています。
//YiiBaseのsingleton部分。
public static function setApplication($app)
{
if(self::$_app===null || $app===null)
self::$_app=$app;
else
throw new CException(Yii::t('yii','Yii application can only be created once.'));
}
setApplicationメソッドで2回目以降のインスタンス化は例外を投げるようになっています。
Yiibaseのstatic変数$_appにCWebApplicationのインスタンスを代入します。
こうなっているのでYiiではYii::app()でいつでもCWebApplicationのメソッドを使えるということなのです。
グローバルに使えてしかも常に一つのインスタンスなので、無駄にnewするコストが大幅に減ります。
そしてconfigureなどもかねているため、Yii::app()はsingletonに適していると言えそうです。
Yiiの場合はしかし、CApplicationクラスで__cloneと__wakeupに対して何も行っていないため、厳密にはsingletonパターンが保証する絶対に一つだけのインスタンスという点は担保できていないので注意が必要かもしれません。
PHP the right wayのsingletonでは上記のマジックメソッドはprivateとして定義しているため、やろうにもできないようにしてあります。
$obj = Yii::app();
//本来ならインスタンスは1個のみなので出来ないはずだが複製できてしまう。
$obj2 = clone $obj;
var_dump($obj === $obj2); // false
CApplicationにprivateメソッドの__cloneと__wakeupをおけば解決するのですが、意図的に可能にしているのでしょうかね?
追記)
$obj = Yii::app()-hoge;
$obj2 = clone $obj;
$obj->property = 'hogehoge';
echo $obj->property; // hogehoge
echo $obj2->property; // hogehoge ????
なるほど、わからん。
singletonにおけるデメリット
singletonの性質上、うまくはまれば役に立つのは間違いありませんが、singletonたるゆえの弊害もあります。
暗黙の依存関係を生んでしまいテストがしにくくなる
グローバルな状態を持ち込む事になる。
本来の使い方がそうなので仕方ないと言えば仕方ない
後から複数インスタンスにしたいってなった時は悲惨
悲惨