web-dev-qa-db-ja.com

シングルトンは本当にそんなに悪いのですか?

重複の可能性:
シングルトンの何が悪いのですか?

多くのデザインパターンが悪用される場合があることは理解できます。また、ママがいつも言っているように、「良いことの多くは必ずしも良いわけではありません!

私は最近、シングルトンを頻繁に使用していることに気付いています。自分でデザインパターンを悪用し、悪習のような習慣にどんどん深く踏み込んでいるのではないかと心配しています。

ユーザーが作業している間、非常に大きな階層データ構造がメモリに保持されるFlexアプリケーションを開発しています。ユーザーは必要に応じてデータをロード、保存、変更、更新できます。

このデータは、いくつかのArrayCollections、Arrays、値オブジェクト、およびゲッターとセッターを介して公開されている他のいくつかのネイティブメンバー変数を集約するシングルトンクラスによって集中化されます。

アプリケーションの任意の場所からデータへの参照を取得するには、Model.getInstance()メソッドタイプ全体を実行します。これは、誰もが知っていると思います。これにより、設計時に、アプリケーションの存続期間中にインスタンスが存在できるのは1回だけであると述べたため、常に同じデータのコピーを手に入れることができます。

この中央データリポジトリから、たとえばプロパティ変更イベントをディスパッチしたり、中央データを参照する複数のUIコンポーネントを作成したり、表示を更新して発生したデータ変更を反映したりすることが簡単にできます。

これまでのところ、このアプローチは効果的であり、私たちの状況に対して非常に実用的であることが証明されています。

しかし、新しいクラスを作成するときは、少し熱心すぎると感じています。クラスがシングルトンであるのか、それともファクトリーを使用するなどの別の方法で管理するのかなどの質問は、少し不確実で、時々少し難しくなる傾向があります。

シングルトンでどこに線を引くのですか?シングルトンをいつ使用し、いつシングルトンから離れるかを決定するための良いガイドラインはありますか?.

また、誰もがデザインパターンについての良い本を推薦できますか?

51

覚えておくべき重要なことは、デザインパターンは抽象的な概念を理解するのに役立つツールにすぎないということです。その理解が得られたら、本からの「レシピ」だけに限定することは無意味であり、目的に最も適したコードを書く能力を傷つけます。

とは言っても、GoFのような本を読むことで、問題について考える方法が増え、自分で何かを実装するときが来たときに、問題に取り組むための幅広い視点を持つことができます。

あなたの場合、シングルトンを使用することがすべての場合に意味がある場合は、すぐに進んでください。それが「ソート」に適合し、不格好な方法で実装する必要がある場合は、新しいソリューションを考え出す必要があります。完全ではないパターンを強制することは、丸い穴に正方形のペグを打ち込むようなものです。

「このアプローチは効果的であり、私たちの状況に非常に実用的であることが証明されている」とあなたが言うなら、私はうまくやっていると思います。

ここに良い本があります:

Gang of Four Book -デザインパターンの古典的な本

Head First Design Patterns -これは数人が代替案として推奨していると聞きました

37
mandaleeka

はい、シングルトンは悪いです。それらはあなたのためにすべてが2つの特性を結合することであるので、それらは悪いです。 (これは、平均して、シングルトンが99.75%の確率で不良であることを意味します;))

GoFで定義されているシングルトンは、次のようなデータ構造です。

  1. オブジェクトへのグローバルアクセスを許可します。
  2. オブジェクトのインスタンスは1つしか存在できないことを強制します。

最初のものは一般的に悪いことと考えられています。グローバルは好きではありません。 2番目は少し微妙ですが、一般的に、これがenforceに対する妥当な制限であるケースは事実上ありません。

場合によっては、オブジェクトのインスタンスを1つ持つことだけが理にかなっています。その場合は、1つだけ作成することを選択します。強制するためにシングルトンは必要ありません。

そして、通常、インスタンスを1つだけ持つことが「理にかなっている」場合でも、結局は意味がないことがわかります。遅かれ早かれ、複数のロガーが必要になります。または、複数のデータベース。または、ユニットテストごとにリソースを再作成する必要があります。つまり、リソースを自由に作成できる必要があります。結果を理解する前に、コードから柔軟性を時期尚早に取り除きます。

シングルトンは依存関係を隠し、結合を増やします(すべてのクラスはシングルトンに依存する可能性があります。つまり、すべてのシングルトンを再利用しない限り、クラスは他のプロジェクトで再利用できません)。これらの依存関係は(関数/コンストラクターパラメーターとして)すぐには表示されないため)、私たちはそれらに気づかず、通常、それらを作成するときにそれについて考えません。シングルトンをプルするのはとても簡単で、ほとんどローカル変数として機能するため、いったんそこにあると、それらを頻繁に使用する傾向があります。そして、それらを再び削除することはほとんど不可能になります。最終的には、おそらくスパゲッティコードではなく、スパゲッティ依存関係グラフになります。そして遅かれ早かれ、暴走する依存関係はシングルトンが互いに依存し始め、初期化が試みられると循環依存関係を取得することを意味します。

単体テストは非常に困難です。 (シングルトンオブジェクトの関数を呼び出す関数をどのようにテストしますか?実際のシングルトンコードを実行する必要はありませんが、それを防ぐにはどうすればよいですか?

はい、シングルトンは悪いです。

時には、あなたは本当にグローバルが欲しいです。次に、シングルトンではなくグローバルを使用します。

時々、非常に非常にまれに、クラスの複数のインスタンスの作成がエラーであり、エラーを引き起こさずにできない場合があります。 。 (私が考えることができる唯一のケースについて、そしてそれが不自然であるとしても、ハードウェアデバイスを表す場合です。GPUが1つしかないため、それをコード内のオブジェクトにマップする場合は、インスタンスは1つしか存在できないことを意味します)。ただし、このような状況(さらに強調すると、複数のインスタンスが重大なエラーを引き起こす状況であり、「複数のインスタンスのユースケースを考えることができない」状況だけではない状況)に気づいた場合は、強制します。その制約ですが、オブジェクトをグローバルに表示することなく実行します。

これらの2つのプロパティはそれぞれ、まれなケースで役立つ場合があります。しかし、私はそれらの組み合わせが良いことになる単一のケースを考えることはできません。

残念ながら、多くの人々は「シングルトンはOOP準拠のグローバルである」という考えを持っています。いいえ、そうではないです。彼らはまだグローバルと同じ問題に苦しんでいますさらに他の完全に無関係なものを導入することへの==。単純な古いグローバルよりもシングルトンを選ぶ理由はまったくありません。

110
jalf

ソフトウェア開発者は、理想的なコーディングスタイルと実用的なコーディングスタイルのどちらを好むかに応じて、2つの陣営にかなり均等に分かれているようです。

  • Idealistic:Neverシングルトンパターンを使用します。
  • 実用的:Avoidシングルトンパターン。

個人的には、実用的なアプローチを好む。時にはルールを破ることが理にかなっていますが、自分が何をしているかを本当に理解していて、関連するリスクを受け入れる用意がある場合に限ります。特定の使用例に関する以下の質問に「はい」と答えることができる場合、シングルトンパターンはいくつかの実用的な利点をもたらすことができます。

  • シングルトンはアプリの外部にありますか?データベース、キューイングサービス、ESBはすべて、シングルトンパターンの完全に有効なマクロの例です。
  • KISS:アプリ全体が2〜3個の内部シングルトンに制限されていますか?
  • DRY:これらのシングルトンは本質的にグローバルなので、アプリ内のほとんどすべてのオブジェクトに参照を組み込む必要がありますか? (例:ロガーまたはコンポーネントメディエーター)?
  • シングルトンは、相互に依存しているか、動作環境に依存していますか?
  • メモリ管理の考慮事項を含め、各シングルトンの適切な起動およびシャットダウンシーケンスを確認しましたか?たとえば、「グランドセントラル」スタイルのスレッドプールでは、操作対象のオブジェクトが有効な状態にある場合にのみタスクが実行されることが保証されるように、main()にインスタンスRun()メソッドとShutdown()メソッドが必要になる場合があります。
14
kgriffs

シングルトンはプログラムを殺さない、プログラマーはプログラムを殺す。

他のプログラミング構造と同様に、適切に使用すれば、足元を狙うことはありません。

推奨される本は結構ですが、シングルトンの使用をいつ選択するかについての経験に伴う十分な背景が常に得られるとは限りません。

その経験は、シングルトンが複数のインスタンスを必要とするときに不適切な選択であることがわかり、突然、あらゆる場所にオブジェクト参照を挿入するのに多くの問題が発生した場合にのみ発生します。

場合によっては、先に進んでオブジェクト参照を用意する方が良い場合もありますが、シングルトンを使用しているという事実は、別の設計にリファクタリングする必要がある場合に直面するであろう問題の範囲を特定するのに役立ちます。これは非常に良いことだと思います。つまり、クラスがまったくない(たとえデザインが不十分であっても)だけでも、クラスへの変更の影響を確認することができます。

9
Cade Roux

基本的に同じ質問に直面しているプロジェクト、つまり、モデルにアクセスする方法、特にそのルート要素にプロジェクトを開始しました。プロジェクトはFlexアプリではなく、遊びです!ウェブアプリですが、それは本当に重要ではありません。

システムに一意の単一オブジェクトがあることは問題ありませんが、問題はそれにアクセスする方法です。したがって、シングルトンに関する議論は、依存性注入(DI)の概念、およびオブジェクトの取得方法に関連しています。

DIの主な引数は次のとおりです。

  • テスト容易性とモック
  • オブジェクトのインスタンス化を使用法から切り離す(ライフサイクル管理につながる可能性がある)
  • 関心事の分離

DIの可能なアプローチは次のとおりです(Fowlerの古典的な article を参照):

  • メソッドパラメータでオブジェクトを渡す
  • サービスロケーター
  • DIフレームワーク

この観点では、シングルトンパターンはサービスロケータの一種にすぎません。 Model.getInstance()

ただし、将来の変更に備えて最大の柔軟性を提供するには、一意のオブジェクトへの参照をできるだけ渡さなければならずModel.getInstance()を使用して取得できるのは、必要。これにより、よりクリーンなコードが生成されます。

4
ewernli

私の意見では、シングルトンの使用は設計上の欠陥を直接示しています。その理由は、C++に組み込まれている通常のオブジェクト作成および破棄メカニズムをバイパスできるためです。オブジェクトが別のオブジェクトへの参照を必要とする場合は、構築時にそのオブジェクトへの参照を渡すか、内部でそのオブジェクトの新しいインスタンスを作成する必要があります。ただし、シングルトンを使用すると、作成とティアダウンのサイクルが明示的に難読化されます。関連する問題は、シングルトンの寿命を制御することが非常に難しいことです。その結果、一般的なシングルトン実装を含む多くのパッケージには、不格好なオブジェクトライフタイムマネージャーなども含まれます。これらは、シングルトンを管理するためだけに存在するのではないかと思うことがあります。

基本的に、オブジェクトを多くの場所で使用する必要がある場合は、スタック内の最も高い共通ポイントで明示的に作成し、参照を介してそれを使用するすべての人に渡す必要があります。シングルトンを使用するときは、複数の引数を新しいスレッドに渡すときに問題が発生することがありますが、これに当てはまらず、スレッドの引数を明示的に定義して、同じ方法で新しいスレッドに渡します。プログラムのフローがよりクリーンになり、静的な初期化の依存関係や誤った分解による厄介な驚きはありません。

3
Andy B

私はこれが古いスレッドであることを知っていますが、OPが実行しようとしていたことに適合する実際のパターンについては誰も言及していないようです。彼が必要性について述べていると私が信じているのは メディエーターパターン と呼ばれるものです。 SourceMakingは、この種の情報を学習/参照するための素晴らしいサイトです。間違いなく、ソフトウェアパターンを人々に紹介する場所に行きます。また、一般に、設計パターンは必ず本質的に良好または悪であるという概念を理解しないことをお勧めします。それらにはすべて用途があります。それは、いつどこでそれらを使用するかを学ぶことだけがコツです。シングルトンを決して使用しないと述べている人々は、私にとって、その有用性を理解していません。

2
tnicks

シングルトンは確かに悪くありません。それらには用途があり、いくつかは非常に優れています。シングルトンは、多くの場合、最初に知った最初の設計パターンであり、かなり単純なので、シングルトンは経験の浅い開発者によって使いすぎられる傾向があります。

シングルトンを使いたいときはいつでも、なぜそれをやっているのか、そしてこのパターンを使うことの利点と欠点は何かを考えてみてください。

シングルトンはグローバルにアクセス可能な「もの」(データまたはメソッドのいずれか)のセットを効果的に作成します。私は、あまりにも多くのグローバル変数を使用することは素晴らしいアイデアではないことにほとんどの人が同意するでしょう。クラスとオブジェクト指向の要点は、すべてを1つの大規模なグローバル空間にまとめるのではなく、物事を個別の領域にグループ化することです。

シングルトンよりも好む傾向がある「パターン」の1つは、必要なオブジェクトを上から下に渡すことです。私はアプリの初期化フェーズ中に一度作成し、それらにアクセスする必要があるすべてのオブジェクトに渡します。シングルトンパターンの「単一作成」部分を模倣しますが、「グローバル」部分はありません。

シングルトンの要点は、オブジェクトが1つだけ存在する必要があるということです。クラスのデータコントロールセットについて言及します。おそらく、実際には、アプリが2セットのデータコントロールクラスを作成する必要がある場合があるため、おそらくこれにシングルトンを適用するのは適切ではありません。代わりに、アプリの初期化でこれらのデータクラスを作成し、それらを渡した場合、現在のアプリで必要なものとして1セットのみを作成しますが、2番目のセットが必要な場合は、いつかその可能性を開いたままにします。簡単に作成できます。また、データコントロールクラスは、アプリ内のどこからでもグローバルにアクセスできる必要があります。私はそうではないと思います。代わりに、それらはおそらく下位レベルのデータアクセス層からのみアクセス可能であるべきです。

一部の人々はGOFの本を推奨しています。確かにこれはすばらしい本ですが、まず一般的なアーキテクチャに関する本を見つけて、まず2/3/n層の設計、カプセル化、抽象化、およびこれらの種類の原則について読んでください。これにより、GOFが語るパターンの適切な使用法を理解するためのより強固な基盤が得られます。

[編集:シングルトンバリアントが役立つもう1つのケースは、何かへの単一のアクセスポイントが必要な場合ですが、実装の詳細は実際には複数の場合があります。呼び出し側は、シングルトンオブジェクトに対する要求が実際には複数の使用可能なオブジェクトに対して解決され、1つが返されることを知る必要はありません。私はここでスレッドプールのようなものを考えています、使用はどこに行くのですか、ちょっと、私にスレッドを取得してください、私は1が必要ですが、私はどちらでもかまいません]

2
Simon P Stevens

グーグルは 確信 シングルトンは悪い考えだと思われる。

これは、Googleのすべてが完璧であったり、彼らのすべての意見が議論の終わりであることを示唆するものではありませんが、このシングルトン検出器を作成して根絶するまでに至っています。あなた自身の決心をしてください。

0
duffymo

いいえ、必ずしも悪いわけではありません。

本の場合、 classics で始める必要があります。

0
Stu

シングルトンは「それほど悪い」ではありません。関連するシングルトンがたくさんあり、ファクトリを使用して、気になるものを失うことなく、それらのシングルトンを置き換えたり統合したりできる場合は、そうすべきです。

本に関しては まあ、ある種のキヤノンがある

0
chaos
0