今回はsingletonデザインパターンを親クラスで実装する方法を紹介します。
普段singletonを使用しているクラスを抽象クラスにした際に「singletonってどうすれば…」となり調べることになったのでメモとなります。
通常のsingleton
通常は差異あれど以下のような形になると思います。
class Singleton {
private static $instance = null;
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {}
}
コンストラクタをprivateにし、プロパティにインスタンスを持つようにします。
インスタンス生成は別途関数を用意し、すでにプロパティにセット済であればプロパティを返し、背っとされていなければ自クラスのインスタンスを生成しプロパティにセットします。
継承すると何が問題?
上記のSingletonクラスを継承すると何が問題になるでしょうか。一見問題なさそうなので実行まで気づきにくい部分でもあります。
答えは「継承したクラスでget_instanceを実行しても継承したクラスのインスタンスが生成されず、Singletonクラスのインスタンスが生成されてしまう」です。
コードにするとわかりやすいと思います。「get_class」はオブジェクトのクラス名を返します。
class Extends_Singleton extends Singleton {
}
$extends_singleton_instance = Extends_Singleton::get_instance();
get_class( $extends_singleton_instance ); // Singleton
これでは意図したものとは異なるので継承した場合でも使用できるように修正します。
親クラスでsingletonを使うには
親クラスでsingletonパターンを利用するには少し工夫をします。まずはコードから。
class Singleton_Parent {
private static $instances = [];
final public static function get_instance() {
$class = get_called_class();
if ( ! isset( self::$instances[ $class ] ) ) {
self::$instances[ $class ] = new $class();
}
return self::$instances[ $class ];
}
private function __construct() {}
}
インスタンスを保持する変数をnullではなく空の配列とします。
「get_instance」関数では「get_called_class」で呼び出し元のクラス名を取得します。先ほど用意した変数にキーを呼び出し元クラス名、値を呼び出し元クラスのインスタンスとして格納します。
こうすることで子クラスの方では変更なくsingletonの形式を使用することが可能になります。
class Extends_Singleton extends Singleton_Parent {
}
class Extends_Singleton_2 extends Singleton_Parent {
}
$extends_singleton_instance = Extends_Singleton::get_instance();
$extends_singleton_instance_2 = Extends_Singleton_2::get_instance();
get_class( $extends_singleton_instance ); // Extends_Singleton
get_class( $extends_singleton_instance_2 ); // Extends_Singleton_2