48

可能な重複:

シングルトンの悪いところは何ですか?

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

私は最近、シングルトンを多用していることに気づいています、そして私は自分自身でデザインパターンを悪用し、そして悪い習慣の種類のより深く実行するかもしれないことを心配しています。

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

このデータは、いくつかのArrayCollections、Array、valueオブジェクト、およびゲッターとセッターを介して公開される他のネイティブメンバー変数を集約するSingletonクラスによって一元管理されます。

アプリケーション内のどこからでも私たちのデータへの参照を得るために、私たちはModel.getInstance()メソッド全体のことをします。設計時に、アプリケーションの存続期間中に存在できるインスタンスは1回だけであると説明していたため、これにより、常に同じデータのコピーを確実に手に入れることができます。

この中央データリポジトリから、たとえばプロパティ変更イベントを送出し、中央データを参照する複数のUIコンポーネントを持ち、発生したデータ変更を反映するように表示を更新することができます。

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

しかし、私は新しいクラスを作成するとき、私はちょっとやり過ぎだと思っています。クラスがシングルトンであるべきか、あるいはファクトリを使用しているような別の方法で管理されるべきかというような質問は、少々不確実性を伴いながら、時々少し困難になる傾向があります。

シングルトンでどこに線を引くのですか?シングルトンをいつ使用するか、またいつ使用しないかを決定するための適切なガイドラインがありますか。

また、デザインパターンに関する優れた本を誰かが推薦できますか?


  • これと似たような質問がいくつかありますが、そのうちのどれかが「完全に重複する」と見なされる可能性があるかどうかはよくわかりません... - Zifre
  • 5分以内に同じ本を推薦する3人が記録になるはずです。そしてヒント:-) - Stu
  • いずれにしても、問題は過剰(ab-use)です。 - BlackTigerX
  • 私の$ 0.02は、Flexでは、愚かなフープが飛び越えることを余儀なくされているため、ほとんどの堅実な設計原則が枠を超えて出ていることです。主な例として、HierarchicalCollectionViewインターフェイスを紹介します。 - rmeador

12 답변


35

覚えておくべき重要なことは、デザインパターンは抽象概念を理解するのを助けるための単なるツールであるということです。理解した後は、本の中の「レシピ」に限定することは無意味であり、自分の目的に最も適したコードを書くことができなくなります。

とは言っても、GoFのような本を読むことは問題を考えるためのより多くの方法をあなたに提示するでしょう。

あなたの場合、シングルトンを使うことがすべての場合に意味があるならば、それからすぐに先に行きなさい。それが「種類の」適合し、あなたがそれを何らかの不格好な方法で実装しなければならないならば、あなたは新しい解決策を思いつく必要があります。完璧ではないパターンを強制することは、丸い穴に四角い釘を打つのと似ています。

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

ここにいくつかの良い本があります:

4冊のギャング - デザインパターンの古典的な本

ヘッドファーストデザインパターン - これは代替手段として少数の人々によって推奨されていると聞いた


  • 本当にありがとうございました。私は間違いなくこれらすべての本のコピーを入手し、それらを介して私のやり方で作業します。 「Gang of Four Book」について聞いたことがあると思います。どこかに。 - josef.van.niekerk
  • GoFの本の残りの部分の大部分は大丈夫ですが、シングルトンの問題に関しては、私は真剣に彼らが違法な何かを吸っていたに違いないと思います。それともOOPに勝つために、古い手続き型プログラマ(彼らのグローバルを本当に欲しい)への譲歩でした(" OOPでグローバルを作ることもできます!私たちはそれらをシングルトンと呼びます)。 - jalf
  • ええ、私はjalfが言っていることから2番目に、シングルトンをグローバル変数の単なるバージョンとして使用することに注意してください。 - Simon P Stevens
  • シングルトンは常に再入力不可能なコードの兆候です。ほとんどの場合、シングルトンはテスト不可能なコードの兆候でもあります。メンテナンスの側面が問題にならない場合は、それらの存在が市場投入までの時間の短縮で正当化されます。ロガーであっても、クラスがデストラクタからログを取っていてシングルトンロガーが既になくなっていると、C ++では有効なシングルトンユースケースと見なされてもアプリケーションがクラッシュする可能性があります。 - bobah

108

はい、シングルトンは悪いです。彼らがあなたのためにするのは2つの特性を結合することだけであるのでそれらは悪いです、それぞれの特性は時間の約95%悪いです。 (これは、平均して、シングルトンの99.75%が悪いことを意味します。)

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

  1. オブジェクトへのグローバルアクセスを許可します。
  2. オブジェクトのインスタンスが1つだけ存在できるようにします。

最初のものは一般的に悪いことと考えられています。グローバルは好きではありません。 2番目の方法はもう少し微妙ですが、一般的にこれが合理的な制限であるケースはほとんどありません。実施する

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

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

シングルトンは依存関係を隠し、結合を高めます(すべてのクラスがシングルトンに依存する可能性があります。つまり、すべてのシングルトンを再利用しない限り、クラスは他のプロジェクトで再利用できません)。 )、それらに気づかないし、通常それらを作成するときそれについて考えない。ただシングルトンを引き込むのはとても簡単です、それはほとんどローカル変数として、そしてすべてのように振る舞います、それで我々は彼らがそこにいたら一度それらをたくさん使う傾向があります。そしてそれはそれらを再び除去することをほとんど不可能にします。あなたは、おそらくスパゲッティコードではなく、スパゲッティディペンデンシーグラフで終わるでしょう。遅かれ早かれ、あなたの暴走した依存関係はシングルトンがお互いに依存し始めることを意味するでしょう、そしてそれから1つが初期化されようとすると循環依存関係を得るでしょう。

それらはユニットテストを非常に難しくします。 (シングルトンオブジェクトの関数を呼び出す関数をどのようにテストしますか?実際のシングルトンコードを実行したくないのですが、どうすればそれを防ぐことができますか?)

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

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

ごくまれに、クラスのインスタンスを複数作成するとエラーになることがあります。できるエラーを引き起こすことなく実行することはできません。 (私が考えることができる唯一のケースについて、そしてそれが人為的に考えられているとしても、あなたが何らかのハードウェアデバイスを代表しているならば。あなたは1つのGPUしか持っていない。存在できるインスタンスは1つだけです)。しかし、もしあなたがそのような状況(そして繰り返しになりますが、「複数のインスタンスのユースケースを考えることができない」という状況だけでなく、複数のインスタンスが重大なエラーを引き起こす状況)にいるのなら、この制約はありますが、オブジェクトをグローバルに表示することもしません。

これら二つの特性のそれぞれできるまれに便利です。しかし、私は単一のケースを考えることはできません。組み合わせそれらのうちの良いことでしょう。

残念ながら、多くの人が「シングルトンはOOPに準拠したグローバルだ」という考えを持っています。いいえ、そうではないです。彼らはまだグローバルと同じ問題を抱えている、加えてまったく関係のない他のものを紹介すること。普通のグローバルよりシングルトンを好む理由は絶対にありません。


  • シングルトンを強制することが批判的なアイディアである時が何度もあります。データベース接続プールを考えます。利用可能なプールがある場合は接続を開く可能性があるため、2つの別々のプールは必要ありません。 - Kekoa
  • 単体テストで2つの別々のプールが必要にならないのはなぜですか。別々のデータベースに2つの別々のプールが必要にならないのはなぜですか。 2つの別々のタスクがあり、それらが互いに飢餓状態になることを許可されていない場合、なぜ2つの別々のプールが必要にならないのでしょうか。 1つのタスクがそれ自身のプール内のすべての接続を占有する可能性があるので、私は他のためにいくつかを予約したいと思います。でもあなたは行う絶対に積極的に1つのプールだけが欲しいのですが、なぜそれを強制するためにシングルトンが必要なのですか?よくしますか偶然に新しい接続プールを作成しますか?もちろん違います。 1つだけ必要な場合は、つくる1。 - jalf
  • +1よく言ったことはありません。まさに私の同僚の気持ちや教育方法です。多くの点が単状態パターンや(ab)グローバル静的メソッドの使用にも当てはまります。 - gix
  • 問題1は本当に致命的な問題です。コード全体に振りかけてしまうと修正が非常に難しいからです。 - starblue
  • @user反対側は、あなたの同僚の常識はおそらくあなたと同じくらい良いということです。 1つのインスタンスが存在することを意図したオブジェクトを書きました。彼は2番目のインスタンスを作成しました。誰があなたがそれを言うのですか彼は間違っている? 2番目のインスタンスを作成することが理にかなっている状況に遭遇しましたが、あなたは予期していませんでした。それがシングルトンだったならば、あなたの同僚は彼が理にかなっていると思ったことをすることができなかったでしょう。私の主張は、「2番目のインスタンスが存在するはずですか」ということです。クラスがある場合にのみ合理的に行うことができる決定です。中古それが最初に設計された時ではありません。 - jalf

14

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

  • 理想的:しないシングルトンパターンを使用してください。
  • 実用的:避けるシングルトンパターン

個人的には、私は実用的なアプローチを好みます。規則を破ることが理にかなっていることもありますが、それは自分がしていることを本当に理解していて、関連するリスクを受け入れても構わないという場合に限られます。あなたがあなたの特定のユースケースに関して以下の質問に「はい」と答えることができるならば、シングルトンパターンはいくつかの実用的な利益を生み出すことができます。

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


  • シングルトン中毒をより社会的に受け入れられるようにするための素晴らしい方法です。 「それらを避け、意味がある場合にのみ使用します」。いいえ、しません。あなた自身の説明から、代替手段がほんの少量の作業を必要とするときはいつでも、あなたは至るところでそれらを使用します。 「理想主義」と「理想主義」の違いは違います。と「実用的」実はシングルトンを避けるようにしてください(私はしません)必要私のデータベース接続のためのシングルトン、例えば)いう彼らは「避ける」。実際には、「正しいことだと思うときに使用する」という意味です。 - jalf
  • @jalf重要なのは、率直に考えて、シングルトンの長所と短所を把握し、次に適切なツールを選択することです。私の哲学は、開発者がそれを十分に防御できる限り、どの開発者でも設計パターンを使用できるということです。 C#の世界では、次のようなものを使用するのが臭いと思われます。unsafeそしてgotoほとんどの場合、まだどちらも.NET自体のソースコードで半頻繁に使用されています。すべての選択肢の長所と短所を知っていて、それでもシングルトンを使用することにした場合は、それで問題ありません。 - Abion47
  • 開発者の能力や考え方についての私の心には、赤い旗を立てるものは何もありません。決して行われる、期間、議論の終わり。私にとっては、開発者が特定の考え方に縛られていることを示しているだけで、競争が激しく進化し続けるソフトウェア開発の世界に遅れずについていくのに十分柔軟に対応できないのではないかと心配しています。私は自分の開発チームが時代遅れの技術や慣習を使ってアイアンフィストで支配チームを統治しながらも、何も考えずに変化についての議論をしゃべったという話をたくさん聞きました。 - Abion47

9

シングルトンはプログラムを殺しません、プログラマーはプログラムを殺します。

他のプログラミング構成体と同様に、適切に使用された場合、あなたは自分の足を撃たないでしょう。

推奨されている本は問題ありませんが、シングルトンを使用することを選択する可能性がある場合の経験に付随する十分な背景を常に提供するわけではありません。

複数のインスタンスを持つ必要があるときにシングルトンが不適切な選択であることがわかったときに初めてその経験が得られます。突然、オブジェクト参照を至る所にインジェクトするのは大変なことになります。

場合によっては、先に進んでオブジェクト参照を配置したほうがよいですが、Singletonをまったく使用しているという事実は、別の設計にリファクタリングしなければならない場合に直面する問題の範囲を特定するのに役立ちます。これは非常に良いことだと思います。つまり、クラスがまったく設計されていなくても、クラスを作成するだけで、クラスへの変更の効果を確認できるようになります。


4

私たちは基本的に同じ質問に直面しているというプロジェクトを始めました。モデルにアクセスするそして、特にそのルート要素。このプロジェクトはFlexアプリではなく、遊びです。 Webアプリですが、それは問題ではありません。

を持っている単一オブジェクトシステムで結構です、問題はアクセス方法。だからシングルトンについての議論はの概念に関連しています依存性注入(DI)、およびオブジェクトの入手方法

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

  • テスタビリティとモッキング
  • オブジェクトのインスタンス化と使用法の分離(ライフサイクル管理につながる可能性がある)
  • 関心事の分離

DIのための可能なアプローチは(古典を見なさい記事ファウラーから):

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

この観点では、シングルトンパターンは単なる一種のサービスロケータです。Model.getInstance()

しかし、将来の変化に備えて最大限の柔軟性を提供するためには、固有のオブジェクトへの参照を次のようにします。回った可能な限り多くのModel.getInstance()必要なときだけ。これにより、コードがよりきれいになります。


  • 参照の受け渡しをサポートするために余分な配管が必要になることはありませんか。 - kgriffs
  • ...ログ記録など、事実上すべての単一のコンポーネント/クラスが使用しようとしているものには? - kgriffs

3

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

基本的に、オブジェクトをさまざまな場所で使用する必要がある場合は、オブジェクトをスタック内の最も高い共通点に明示的に作成し、それを使用するすべての人に参照を介して渡します。複数の引数を新しいスレッドに渡すのに問題があるためにシングルトンを使用することがありますが、これには該当せず、スレッド引数を明示的に定義して同じ方法で新しいスレッドに渡します。あなたはあなたのプログラムがずっときれいに流れ、静的な初期化依存関係や誤ったティアダウンによる厄介な驚きがないことに気付くでしょう。


2

シングルトンは確かに悪くないです。彼らは彼らの用途を持っています、それらのいくつかはとても良いです。シングルトンは最初に学んだ設計パターンであることが多く、経験が浅い開発者にとっては使い過ぎになりがちで、かなり単純なので、意味を考えずにいたるところで使用されています。

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

シングルトンはグローバルにアクセス可能な 'もの'(データかメソッドのどちらか)のセットを効果的に作成します、そして私はほとんどの人があまりにも多くのグローバル変数を使うのは素晴らしい考えではないと思うと思います。クラスとオブジェクト指向の重要な点は、すべてを1つの巨大なグローバル空間にまとめるのではなく、物事を個別の領域にまとめることです。

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

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

何人かの人々はGOF本を推薦しました。はい、それは素晴らしい本ですが、最初に一般的なアーキテクチャに関する本を試してみて、2/3 / n層の設計、カプセル化、抽象化、そしてこれらの種類の原則についてまず読んでください。これにより、GOFが話しているパターンの適切な使用方法を理解するためのより強固な基盤が得られます。

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


  • スレッドプールは、シングルトンバックファイアの好例です。 .NETスレッドプールはそのように機能しますが、これは面倒です。つまり、自分のアプリケーションでタスク用のスレッドプールを作成できないということです。私はそれを.NETクラスライブラリ、私が使っているかもしれないサードパーティ製のライブラリと共有しなければならない、そして神は他に何を知っている。私はそれが可能性がどれくらいありそうかを言う方法が全くありません持ってるフリースレッド正当な設計は、スレッドプールクラスを作成することです。誰でも彼らが欲しいならばインスタンス化することができます自分の他のユーザーが何人いるのか気にしなくても使用できる単一のグローバルスレッドプールを公開します。 - jalf
  • @jaif:私はあなたが彼のスレッドプールのポイントを逃したような気がします。フリースレッドを取得したい場合や、もっと制御したい場合は、ThreadクラスやBackgroundWorkerを使用することができます。あなた自身の個人的なユーザ管理のスレッドプールが欲しいなら、プロデューサ - コンシューマパターンの実装を見ることができます(このページの下半分を見てください:albahari.com/threading/part4.aspx)スレッドプールの重要な点は、アプリケーションだけではなく、システム全体で非同期作業のバランスをとることです。 - Simon P Stevens
  • どうして〜しなきゃいけない.NETはすでに私のためにそれをやろうとしたときに、このパターンを自分で実装する必要がありますか?要は、もし彼らがそれをシングルトンとしてハードコーディングしていなければ、システム全体に渡って仕事をしたのではないということです。加えて自分のアプリ内のより小さなスコープで役に立つことに。それは理由もなく柔軟性を取り除くシングルトンの完璧な例です。スレッドプールのポイントを見逃していないので、ちょっとした修正をすれば、はるかに一般的に役立つはずだと言っています。 - jalf
  • 複数のスレッドプールを作成した場合、それらのバランスはどうなりますか。必要なのは違うものだと思います。複数のスレッドプールがどのように機能するのかわかりません。スレッドプールですシステム全体のスレッドプール。アプリ間で作業のバランスをとるために存在します。ですそう、おそらくMSもプロデューサコンシューマスレッドキューを実装する必要がありますが、それらについてはあまり議論しないでください。 、私はMSが彼らのやり方でそれをする理由を持っていると確信しています、私たちはそれを善または悪のために生きなければなりません。 - Simon P Stevens

2

私はこれが古いスレッドであることを知っていますが、誰もOPがやろうとしていたことに合う実際のパターンについて言及しているようには見えませんでした。彼が必要としていると私が信じているものは、メディエータパターン。 SourceMakingは、この種の情報を学習/参照するための素晴らしいサイトです。確かに私はソフトウェアのパターンを人々に紹介する場所に行きます。また、どんなデザインパターンも必然的に善または悪であるという考えに没頭しないことが一般的には良い考えです。それらはすべて彼らの用途を持っています、それはいつ、どこでそれらを使うべきかを学ぶことがトリックです。私にはシングルトンを使用しないと言う人は、その有用性を理解していません。


0

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

本に関しては、あなたはから始める必要があります古典


0

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

本に関しては、まあ、一種の規範があります


0

0

グーグルは確信しているそのシングルトンは悪い考えです。

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

リンクされた質問


関連する質問

最近の質問