Singletonパターンとは
Singleton(シングルトン)パターンは多くの問題の解決に役立つパターンです。
このパターンでは、クラスのインスタンスは必ず1つしか生成されません。そのインスタンスは使用前に必ず初期化され、シングルトンをグローバルアクセスポイントとすることで、設計をシンプルにできます。
こう書いていくと良いことずくめのようですが、この「古典的な」デザインパターンに何か短所はあるのでしょうか。
Singletonにご注意を
実はたくさんあります。
確かにシングルトンパターンは魅力的ですが、実はこのパターンには利点よりも弊害の方が多いと言えます。
まずテストの妨げになります。そして保守性の点でも不利です。残念ながらその事実は広く知られているとは言えないため、多くのプログラマを焚き付けているのです。つい使いたい誘惑にかられますが、その誘惑に抵抗しなくてはなりません。
具体的な問題例
- 「必要なインスタンスは1つだけ」という要望は、多くの場合推測にすぎない
アプリケーションの設計段階では「インスタンスは絶対に1つしかいらないだろう」と考えていても、後にそうもいかなくなることが多いです。
アプリケーションを設計する時にそういう推測をあちこちでしていると、どこかで大きな問題に直面することになります。要件は変化するものであり、良いデザインとはそれを織り込んでいるもののことを言います。
シングルトンパターンは、要件の変化を織り込んでいるとは言えません。
- 理論的には独立しているはずのコード間に暗黙の依存関係を生んでしまう
暗黙的な依存関係を生み出すことの問題は、まず依存関係が一見してわかりにくいからです。
また、生まれてしまう依存関係は本来不必要なはずで、問題の存在がはっきりするのは、単体テストの時です。
単体テストでは疎結合が前提となっていて、また実装コードの代わりに、適宜モック(テスト用の仮置き)実装に差し替えられることも多くあります。シングルトンパターンはその妨げになるのです。
- シングルトンパターンは永続化された状態を暗黙のうちに伴う
これも、単体テストの妨げになります。単体テストは、どのような順序でもテストが実行できるために互いに独立でなければなりません。
そしてどのテストにおいても実行前には必ずプログラムを既知の状態に設定できるはずなのです。
不変でない状態を伴うシングルトンを導入してしまうと、それが難しくなります。
加えてグローバルにアクセス可能で、かつ永続化された状態があるとコードから状態の推論が困難になります。特に、マルチスレッド環境で顕著になります。
- マルチスレッド環境での使用は特に危険が大きい
マルチスレッドでは、シングルトンオブジェクトへのアクセスにはロックが必要になりますが、単純なロックでは効率が良いとは言えないため、いわゆるDCL(Double-Checked Locking)を使うことが増えています。
しかしDCLだと危険性が高まってしまう恐れがあります。多くの言語でDCLPはスレッドセーフでないからです。
- クリーンアップにも危険が潜んでいる
プログラム終了の際には、シングルトンも自動的にクリーンアップされることになります。
ただし、複数のシングルトンがある時、どのような順序でクリーンアップされるかは決まっていません。相互に依存し合うシングルトンがアプリケーションに含まれている時などに問題が活性する恐れがあります。
アプリケーションのシャットダウン時に、あるシングルトンが別のシングルトンにアクセスしようといた時、そのシングルトンはすでにクリーンアップされてしまっている、という事態になる恐れがあるからです。
まとめ
シングルトンパターンの欠点の中には、工夫次第で回避可能なものもありますが、それによってコードが複雑化してしまいます。しかし、シングルトンパターンを選ばなければ、そういった心配は不要です。
シングルトンパターンは、必要なインスタンスが絶対に1つだけであると確信できるクラス以外では使うべきではありません。また、シングルトンをグローバルアクセスポイントとすることも避けるべきです。どこからアクセスされるかわからないグローバルアクセスポイントは好ましくありません。
シングルトンへの直接のアクセスは、あらかじめ決められたごく少数の箇所からのみ行うべきです。他のコードからは、インタフェースを通じてアクセスするようにします。
そして他のコードは、そのインタフェースを実装するのがシングルトンなのか、他の種類のクラスなのかを意識する必要がありません。つまり、シングルトンに依存しない設計になります。これにより、ユニットテストの障害となる依存関係が生じることを防ぎ、保守性も向上します。
今後、シングルトンの実装やシングルトンへのアクセスを検討する際には、ここで述べたようなことをじっくりと考えてみてください。