概要
- 最近、無駄にSingletonが使われているプログラムをメンテナンスする機会があり、非常に残念な思いをしているので、このつらさを世の中に広めないために書きます。
- 他にもSingletonが使われていることによって残念な思いをしている人を探してみましたが、日本語では見当たりませんでした。海外の記事は見つけました。
- 全く同じ理由でSingletonのつらさを感じていたのでそのまま訳します。
1) Singletonはグローバルスコープからの呼び出しによく使われる
正しい。
しかし、何のためにでしょうか?
singletonパターンはあるシステム上で明確に1つだけしか存在しない呼び出しを提供する。よって、サービス内でオブジェクトの参照を持ち回る必要がなくなる。
しかし、そのような使い方は、グローバル変数と何が違うのか?(ご存じの通り、グローバル変数って良くないよね?)
Singletonでは、インターフェイスが公開されていないから、結局、コードを読んで中身のデザインを知り、依存関係やインターフェイスを調べる必要がある。singletonを使うことによってクラスのデザインやインターフェイスが不明瞭になる。
参照を持ち回らなくて良くなる代わりに、微妙なシステムデザインになってしまう。Singletonはグローバル変数機能ではない。
システムデザインを深く考えると、ほとんどの場合、グローバル変数を使わなくて済む(=綺麗な)システムデザインを行える場合が多い。
2) Singletonはオブジェクト生成に制約を設けられる
正しい。
しかし、1クラス1責務のクラスに違反し、2つの責務を1つのクラスに課してしまっている。
クラス自身は、singletonかどうかを気にする必要は無い。
クラスはビジネス要件のみを考えるべきである。
クラスの生成方法を制限したければ、Factoryパターン化、builderパターンをを使ってオブジェクトの生成をカプセル化し、そこで制約を設ければよい。
これにより、1つのビジネス要件から責務と生成を分離出来る。
3) Singletonはクラス間の結合度を高めてしまう
テストしやすいコードにするための1つの基準として、クラス間の結合度を低くするしなければならない。
それにより、モックやスケルトンなどのダミーオブジェクトに簡単に差し替えてテストを行えるようになる。
Singletonは、生成されたオブジェクト内の結合度が高くなるので、ポリモーフィズムやダミーオブジェクトへの置き換えを難しくする。
代替手段として、1つめで指摘するように生成方法を分離することにより、結合度を下げることが出来る。
4) Singletonは状態を持ち続ける
永続性は単体テストの敵。
テストを効率的に行うことは、1つ1つが独立していなければならない。そうで無ければテストが他のオブジェクトに影響されてしまう。
これは、別の場所の影響でテストが失敗してしまう可能性がある。テストは1つを明確にテスト出来ればよい。そうでなければ、バグが埋め込まれるリスクが高まります。
静的な変数を無くすためには、テスト毎に状態変数を持たないようにすべき。Singletonは静的変数を必要としてしまう。Singletonを使わないことは、テストドリブンな開発への基本である。
引用元
私の感想
- Singletonを使うべきでない理由はいくつもありますが、一番は、ユニットテストを書く事が非常に困難になる。
- 書けることは書けるけど、コンテクストを意識したテストを書かなければいけなくなり、書くのもテストをレビューするのも正当性を機械的にチェック出来ない。
- ミドルウェアレベルでSingletonが多用されたコードをDevOpsする仕事がありましたが、テストコードが書けなくて苦しかったです。
- 理想のオブジェクト指向で書かれたアプリケーションは、利用者がnewを呼ぶことで初めてオブジェクトが生成されることが理想と考えます。Singletonを使うことで誰がいつ作ったかよくわからないオブジェクトが作られない状態が良い状態です。
- Singletonで書かれたコードを見つけたらgit blameして、作者にこのページのURLを送りつけるなどして頂ければと思います。
- Singletonパターンって、デザパタの中でも理解しやすいから、「デザパタを使っている俺かっこいい」のたいなノリで多用される。初心者が多用しがち。
- メモリを節約出来るという利点もあるがそれ以上にデメリットが大きすぎる。(使いどころかな。pro/conを整理して使えば良いかなと。)
Comments