web-dev-qa-db-ja.com

シングルトンパターンの代替

シングルトンパターンに関するさまざまな意見を読みました。絶対に避けるべきだと主張する人もいれば、特定の状況で役立つと主張する人もいます。

シングルトンを使用する1つの状況は、特定のクラスAのオブジェクトを作成するためにファクトリー(タイプFのオブジェクトfとしましょう)が必要な場合です。ファクトリーは、いくつかの構成パラメーターを使用して一度作成され、その後、タイプAがインスタンス化されます。したがって、Aをインスタンス化する必要があるコードのすべての部分がシングルトンfをフェッチして、新しいインスタンスを作成します。

F& f                   = F::instance();
boost::shared_ptr<A> a = f.createA();

だから私の一般的なシナリオは

  1. 最適化の理由(複数のファクトリオブジェクトは必要ありません)または共通の状態を共有する(たとえば、ファクトリはそれが作成できるAのインスタンスの数を知っている)ため、クラスのインスタンスは1つだけ必要です
  2. コードのさまざまな場所でFのこのインスタンスfにアクセスする方法が必要です。

私は興味がないこのパターンが良いか悪いかについての議論に参加していますが、シングルトンの使用を避けたい場合、他にどのようなパターンを使用できますか?

私が考えたのは、(1)レジストリからファクトリオブジェクトを取得する、または(2)プログラムの起動中のある時点でファクトリを作成し、ファクトリをパラメータとして渡すことでした。

ソリューション(1)では、レジストリ自体がシングルトンであるため、シングルトンを使用しない問題を工場からレジストリにシフトしました。

ケース(2)で、ファクトリオブジェクトのソースとなる初期ソース(オブジェクト)が必要なため、もう一度別のシングルトン(ファクトリインスタンスを提供するオブジェクト)にフォールバックするのではないかと心配です。このシングルトンのチェーンをたどることで、問題を1つのシングルトン(アプリケーション全体)に減らすことができます。これにより、他のすべてのシングルトンが直接または間接的に管理されます。

この最後のオプション(他のすべての一意のオブジェクトを作成し、適切な場所に他のすべてのシングルトンを挿入する1つの初期シングルトンを使用する)は許容できる解決策ですか?これは、シングルトンを使用しないようにアドバイスしたときに暗黙的に提案されるソリューションですか、それとも他のソリューションは何ですか?上記の例では?

[〜#〜]編集[〜#〜]

私の質問のポイントは一部の人々によって誤解されていると思うので、ここにいくつかの詳細があります。説明したように ここ 、Word singletonは、(a)単一のインスタンスオブジェクトを持つクラス、および(b)そのようなオブジェクトの作成とアクセスに使用されるデザインパターンを示すことができます。

わかりやすくするために、(a)の場合はnique objectを、(b)の場合はsingleton patternを使用します。そのため、シングルトンパターンと依存性注入が何であるかを知っています(ところで、最近DIを多用して、取り組んでいるコードからシングルトンパターンのインスタンスを削除しています)。

私のポイントは、メインメソッドのスタックにある単一のオブジェクトからオブジェクトグラフ全体がインスタンス化されない限り、シングルトンパターンを通じて常にいくつかの一意のオブジェクトにアクセスする必要があるということです。

私の質問は、完全なオブジェクトグラフの作成と配線がメインメソッドに依存している(たとえば、パターン自体を使用しない強力なDIフレームワークを使用する)だけがsingleton-pattern無料のソリューションかどうかです。

27
Giorgio

2番目のオプションは良い方法です。依存関係の注入の一種です。これは、シングルトンやグローバル変数を避けたい場合にプログラム全体で状態を共有するために使用されるパターンです。

somethingでファクトリを作成する必要があるという事実を回避することはできません。その何かがたまたまアプリケーションであるならば、そうです。重要な点は、ファクトリーはそれを作成したオブジェクトを気にする必要がなく、ファクトリーを受け取るオブジェクトは、ファクトリーがどこから来たかに依存すべきではないということです。オブジェクトにアプリケーションシングルトンへのポインターを取得させて、ファクトリーを要求しないでください。アプリケーションでファクトリを作成し、それを必要とするオブジェクトに渡します。

8
Caleb

シングルトンの目的は、特定のレルム内に存在できるインスタンスが1つだけであることを強制することです。つまり、シングルトンの動作を強制する強い理由がある場合は、シングルトンが役立ちます。実際には、これはめったに当てはまりませんが、マルチプロセッシングとマルチスレッディングは確かに「一意」の意味を曖昧にします-それは、マシンごと、プロセスごと、スレッドごと、リクエストごとに1つのインスタンスですか?また、シングルトンの実装は競合状態を処理しますか?

シングルトンの代わりに、私は次のいずれかを使用することを好みます:

  • 有効期間が短いローカルインスタンス。ファクトリーの場合:典型的なファクトリークラスには、存在するとしても最小限の状態があり、それらが目的を果たした後にそれらを存続させる本当の理由はありません。クラスの作成と削除のオーバーヘッドは、実際のすべてのシナリオの99%で心配する必要はありません。
  • たとえば、インスタンスを渡すリソースマネージャーの場合:リソースのロードは高価であり、メモリに保持したいので、これらは長期間有効である必要がありますが、それ以上インスタンスが作成されないようにする理由は絶対にありません。数か月後に2人目のリソースマネージャーを雇う...

その理由は、シングルトンが偽装のグローバル状態であるためです。つまり、アプリケーション全体に高度な結合が導入されます。コードのどの部分でも、最小限の労力でどこからでもシングルトンインスタンスを取得できます。ローカルオブジェクトを使用したり、インスタンスを渡したりすると、より多くの制御が可能になり、スコープを小さく保ち、依存関係を狭くすることができます。

18
tdammers

ほとんどの人(あなたを含む)は、シングルトンパターンが実際に何であるかを完全に誤解しています。シングルトンパターンは、クラスの1つのインスタンスが存在し、アプリケーション全体でコードがそのインスタンスへの参照を取得するためのメカニズムがあることを意味します。

GoFブックでは、静的フィールドへの参照を返す静的ファクトリメソッドは単なる例であり、そのメカニズムは欠点。残念ながら、みんなと彼らの犬はそのメカニズムにとらわれて、それがシングルトンのすべてだと思っていました。

引用する代替案は、実際にはシングルトンでもありますが、参照を取得するためのメカニズムが異なります。 (2)呼び出しスタックのルートの近くの数か所だけで参照が必要でない限り、明らかに多くのパススルーが発生します。 (1)依存性注入の大まかなフレームワークのように聞こえます-では、なぜそれを使用しないのですか?

利点は、既存のDIフレームワークが柔軟で強力で十分にテストされていることです。シングルトンを管理するだけではありません。ただし、それらの機能を完全に使用するには、特定の方法でアプリケーションを構造化する場合に最適に機能しますが、常に可能とは限りません。理想的には、DIフレームワークを通じて取得され、すべての依存関係が推移的に入力され、次に実行されます。

編集:結局のところ、とにかくすべてがmainメソッドに依存しています。しかし、あなたの言うとおりです。グローバル/スタティックの使用を完全に回避する唯一の方法は、メインメソッドからすべてを設定することです。 DIは、メインメソッドが不透明でサーバーをセットアップし、アプリケーションコードが基本的にサーバーコードによってインスタンス化されて呼び出されるコールバックで構成されるサーバー環境で最も一般的であることに注意してください。ただし、ほとんどのDIフレームワークでは、中央レジストリへの直接アクセスも許可されています。 SpringのApplicationContext(「特殊なケース」用)。

したがって、基本的にこれまでに人々が思いついた最高のものは、あなたが言及した2つの選択肢の巧妙な組み合わせです。

4

いくつかの選択肢があります:

依存性注入

すべてのオブジェクトには、作成時に依存関係が渡されます。通常、フレームワークまたは少数のファクトリクラスのいずれかが、オブジェクトの作成とワイヤリングを担当します。

サービスレジストリ

すべてのオブジェクトにサービスレジストリオブジェクトが渡されます。さまざまなサービスを提供するさまざまなオブジェクトを要求するメソッドを提供します。 Androidフレームワークはこのパターンを使用します

ルートマネージャー

オブジェクトグラフのルートである単一のオブジェクトがあります。このオブジェクトからのリンクをたどることにより、他のオブジェクトを最終的に見つけることができます。コードは次のようになります。

GetSomeManager()->GetRootManager()->GetAnotherManager()->DoActualThingICareAbout()
3
Winston Ewert

C++やPythonなどのマルチパラダイム言語を使用している場合、シングルトンクラスの代替手段の1つは、名前空間にラップされた関数/変数のセットです。

概念的に言えば、情報の非表示に使用される無料のグローバル変数、無料のグローバル関数、静的変数がすべて名前空間にラップされたC++ファイルは、シングルトンの「クラス」とほぼ同じ効果をもたらします。

継承が必要な場合にのみ機能します。この方法のほうが良かったであろうシングルトンをたくさん見ました。

1
Gort the Robot

シングルトンからのニーズを1つの関数にまとめることができる場合は、単純なファクトリ関数を使用しないのはなぜですか?グローバル関数(例ではおそらくクラスFの静的メソッド)は本質的にシングルトンであり、コンパイラーとリンカーによって一意性が強制されます。

class Factory
{
public:
    static Object* createObject(...);
};

Object* obj = Factory::createObject(...);

確かに、これは、シングルトンの操作を単一の関数呼び出しに還元できない場合に機能しなくなりますが、関連する関数の小さなセットで解決できる場合があります。

そうは言っても、質問の項目1と2は、本当に何かを本当に望んでいることを明確にします。シングルトンパターンの定義に応じて、すでに使用しているか、非常に接近しています。シングルトンでなくても、少なくとも1つに非常に近いものはないと思います。シングルトンの意味に近すぎます。

あなたが示唆するように、ある時点で何かを持っている必要があるので、おそらく問題は何かの単一のインスタンスを持っているのではなく、乱用を防止する(または少なくとも阻止または最小化する)ための措置を講じることです。 「シングルトン」からできるだけ多くの状態をパラメータに移動することから始めるのがよいでしょう。インターフェースをシングルトンに狭めることも、そのように乱用する機会が少ないので役立ちます。時には、それを吸い取って、ヒープやファイルシステムのようにシングルトンを非常に堅牢にする必要があります。

1
Randall Cook

シングルトンのカプセル化

ケースシナリオ。アプリケーションはオブジェクト指向であり、より多くのクラスがある場合でも、3つまたは4つの特定のシングルトンが必要です。

例の前(C++のような擬似コード):

// network info
class Session {
  // ...
};

// current user connection info
class CurrentUser {
  // ...
};

// configuration upon user
class UserConfiguration {
  // ...
};

// configuration upon P.C.,
// example: monitor resolution
class TerminalConfiguration {
  // ...
};

1つの方法は、一部の(グローバル)シングルトンを部分的に削除することです。それらのすべてを、メンバーとして他のシングルトンを持つ一意のシングルトンにカプセル化します。

このように、「いくつかのシングルトンアプローチではなく、シングルトンをまったく使用しないアプローチ」の代わりに、「すべてのシングルトンを1つのアプローチにカプセル化」します。

例の後(C++のような擬似コード):

// network info
class NetworkInfoClass {
  // ...
};

// current user connection info
class CurrentUserClass {
  // ...
};

// configuration upon user
// favorite options menues
class UserConfigurationClass {
  // ...
};

// configuration upon P.C.,
// example: monitor resolution
class TerminalConfigurationClass {
  // ...
};

// this class is instantiated as a singleton
class SessionClass {
  public: NetworkInfoClass NetworkInfo;
  public: CurrentUserClass CurrentUser;
  public: UserConfigurationClass UserConfiguration;
  public: TerminalConfigurationClass TerminalConfiguration;

  public: static SessionClass Instance();
};

これらの例は疑似コードに似ており、軽微なバグや構文エラーは無視して、質問の解決策を検討してください。

使用するプログラミング言語は、シングルトン実装または非シングルトン実装の実装方法に影響を与える可能性があるため、他にも考慮すべき点があります。

乾杯。

0
umlcat

IoCコンテナーの使用についてはどうですか?いくつかの考えをもって、あなたは次のようなものになるかもしれません:

Dependency.Resolve<IFoo>(); 

起動時に使用するIFooの実装を指定する必要がありますが、これは後で簡単に置き換えることができる1回限りの構成です。解決されたインスタンスの寿命は、通常、IoCコンテナーによって制御できます。

静的シングルトンvoidメソッドは次のように置き換えることができます。

Dependency.Resolve<IFoo>().DoSomething();

静的シングルトンゲッターメソッドは次のように置き換えることができます。

var result = Dependency.Resolve<IFoo>().GetFoo();

例は.NETに関連していますが、他の言語でも非常によく似ていることは確かです。

0
CodeART

元の要件:

だから私の一般的なシナリオは

  1. >最適化の理由(複数のファクトリオブジェクトは必要ありません)または共通の状態を共有する(たとえば、ファクトリはまだ作成できるAのインスタンスの数を知っている)ため、クラスのインスタンスは1つだけ必要です。
  2. コードのさまざまな場所でFのこのインスタンスfにアクセスする方法が必要です。

シングルトンの定義(および後で参照するもの)と一致しないでください。 GoF(私のバージョンは1995)ページ127から:

クラスにインスタンスが1つだけあることを確認し、クラスへのグローバルアクセスポイントを提供します。

インスタンスが1つだけ必要な場合は、-precludeにはなりません。

グローバルにアクセス可能な単一のインスタンスが必要な場合は、グローバルにアクセス可能な単一のインスタンスを作成を使用します。あなたがするすべてのために名前を付けられたパターンを持つ必要はありません。そして、はい、通常、単一のグローバルにアクセス可能なインスタンスは悪いです。それらに名前を付けることはそれらを悪くないにしません。

グローバルなアクセシビリティを回避するには、一般的な「インスタンスを作成して渡す」または「2つのオブジェクトの所有者にそれらを接着させる」というアプローチがうまく機能します。

0
Telastyn