シングルトンパターンの使い所を学ぶ
デザインパターンの1種として「シングルトンパターン」というものがある.
シングルトンパターンとは,「インスタンスが1個しか生成されないことを保証する」ことを指す.
具体的にコード例を挙げてみる.
class Singleton {
// インスタンス化されているか判断されているかに使用するメンバ変数
private static $instance;
// コンストラクタをprivateにして外部からアクセスできないようにする
private function __construct(){
echo "インスタンスを生成したよ";
}
// すでに自クラスがインスタンス化されているかを判断する
public static function getInstance() {
if ( !isset(self::$instance) ) {
self::$instance = new Singleton();
}
return self::$instance
}
}
シングルトンパターンを実装する上で必要なのは3つのステップ
- インスタンス化されているかを判断するための変数を用意
- private コンストラクタを実装
- 外部に公開するためのgetInstanceメソッドの中に,自クラスがインスタンス化されていない時だけ新たにインスタンス化するメソッドを実装
この3つのステップを踏めばシングルトンパターンの実装はできる.
ちなみに呼び出したいときは,常にgetInstanceを呼ぶ.
// $singleton1と$singleton2は同じインスタンスが返っている
$singleton1 = Singleton::getInstance();
$singleton2 = Singleton::getInstance();
これが基本的なシングルトンパターンの実装方法である.
シングルトンパターンを実装することのメリット
無駄なインスタンス化によるエラーを防ぐことができる
たくさんのクラスが複雑に継承してある場合,無駄なインスタンス化によって思わぬエラーが発生することがある.
シングルトンパターンを使えばそんな予期しないエラーを未然に防ぐことができる.
メモリの消費量が抑えられる
無駄なインスタンス化が減ることで,メモリが圧迫されず,より軽量な設計が可能になる
継承やインターフェースを実装できる
シングルトンパターンは継承やインターフェースを実装することができる.これのどこがメリットなのかはのちに解説していく
staticとの違い
ここまで学んで一つ疑問に思ったのが,static修飾子つけるのと何が違うのかということだった
つまり,最初に説明したようになるべくインスタンス化したくないクラスを作るのであればstaticつけてしまえば終わりじゃんと思った
しかし,メリットをよく見返してみるとstaticとの違いが見えてきた.
staticとシングルトンパターンの違いは大きく分けて2つ
- 1つしかインスタンス化されないことが「保証」される
- 継承,インターフェースが実装できる
以上2つがstaticとシングルトンパターンの違いだと考えられる.
1つしかインスタンス化されないことが「保証」される
どんなにコメントなどで「このクラスはインスタンス化しないこと」,などと書いていても実装するのは人であるため,その法則が守られる保証はない.それによって様々な実装方法が存在する状況になり,コードの可読性も下がり,バグの温床にもなり得る.しかし,シングルトンパターンを用いればそのクラスは1度しかインスタンス化されないことが保証されているため,そのようなことが起きにくくなる.
継承,インターフェースが使える
シングルトンパターンを用いたクラス自体にももちろん,継承やインターフェースを実装することができます.これは大規模なサービスになればなるほど威力を発揮するもので,継承元のクラスやインターフェースによってクラスが管理できるためサービス全体をみた時の拡張性が広がる場合がある.
シングルトンパターンのデメリット
拡張性に問題が出る場合がある
staticの違いの部分でシングルトンパターンを用いることができれば,拡張性が広がる場合があると述べたが,一方で狭まってしまう場合もあり得る.その原因となるのが将来的にそのクラスが本当に1つのインスタンスを生成することで役割を果たせるのか予測が困難ということがある.だからこそ,シングルトンパターンの使い所を十分に理解し,本当に必要な場面でのみ扱うことが重要である.
ユニットテストがやりにくい
ユニットテストを行う際はクラス間が疎結合であることが前提になっている.つまり,クラスが依存関係にないということである.
具体的に考えてみると,あるクラスに変更を加えた際に,他のクラスにも影響が出ることが前提として設計されていたら一つのクラスを改修しただけなのに影響範囲が大きくなってしまう.そのため,テスト工数も多くなり開発全体で考えたらクラスが疎結合である方が効率が良い.
シングルトンパターンを採用すると理論的には独立しているはずのクラス間の関係に暗黙的な依存関係が生まれてしまう.そのため,ユニットテストを実施する際に邪魔なものになる.
シングルトンパターンの使い所
ここまででシングルトンパターンは,使い所を間違えると将来的に致命的な技術的負債になり得るということがわかった.
そこで,実際にシングルトンパターンをどのタイミングで使うべきなのか考えてみる.
上記のメリット,デメリットから考えると以下の条件を満たす場合にのみシングルトンパターンを使うべきであると考えられる.
- 動的なクラスでない場合
引数やSetterなどにより,値が変化することが考えられる場合は将来的に一つしかインスタンス化できない状況が足枷になることがあり得る. - そのクラスがどのアプリケーションに呼び出されるか意識しない場合
そのクラス自身が他のクラスなどに依存せずに,呼び出され先を意識しないで済むかを考える. - 継承やインターフェースをしたり,されたりするか
staticではできないこと,シングルトンパターンの特徴の一つ - 他のアプリケーションは常にこのクラスの1つのインスタンスを必要としているか
やはり,1つのインスタンスしか生成できないことが将来的にデメリットになる場合が多いためとても重要な条件
上記の場合を全て満たす場合にのみシングルトンパターンを適用する意味が生まれる.
まとめ
シングルトンパターンは使うと便利なデザインパターンであるが,本当に必要なときにのみ使うべきであることがわかった.
つまり,シングルトンパターンを使う理由がある時のみに使うべきであるということである.
上記の使い所をしっかり理解した上で,シングルトンパターンを使うことで将来の技術的負債を生まないようにしたい.