シングルトンの代わりとなるもの


114

アプリケーションの構成情報を保持するクラスがあります。以前はシングルトンでした。いくつかのアーキテクチャのレビューの後、シングルトンを削除するように言われました。さまざまな構成を一度にテストできるため、単体テストでシングルトンを使用しないことにはいくつかの利点があります。

シングルトンなしでは、コード内のあらゆる場所でインスタンスを渡す必要があります。非常に乱雑になっているので、シングルトンラッパーを作成しました。同じコードをPHPと.NETに移植していますが、構成オブジェクトに使用できるより良いパターンがあるかどうか疑問に思っています。

回答:


131

Googleテストのブログは(テスト可能なコードを作成するために)シングルトンを避けることについて一連のエントリがあります。多分これはあなたを助けることができます:

最後の記事では、新しいオブジェクトの作成をファクトリーに移動する方法を詳細に説明しているため、シングルトンの使用を回避できます。確かに読む価値があります。

つまり、すべての新しいオペレーターを工場に移動します。寿命が近いオブジェクトをすべて1つのファクトリにグループ化します。


3
***シングルトンを回避するためにディペンデンシーインジェクションを使用する
ジャスティン

これらの記事は、Google C ++プログラミング標準と同等です。

2
まあ、そうでもない。たとえば、「静的メソッドを使用しない」というアドバイスは、Scott Meyers / Herb Suttersの最小限のインターフェース原則に反するものです。役に立つアドバイスはありますが、それらには複数の心の貢献が欠けています。
Matthieu M.

@FrankSリンクの順序を入れ替えた理由は?最初は年代順でした。
cregox 2013年

@Cawasは実際には考えていません。それは4年以上前だったので、当時はいくつかの理由があったと思います:-)
FrankS

15

最善の方法は、代わりにFactoryパターンを使用することです。(ファクトリで)クラスの新しいインスタンスを構築するとき、単一のインスタンス(ファクトリクラスに格納する)への参照として、または関連する新しいオブジェクトへのデータ。

すべてのオブジェクトには、シングルトンでの使用に使用されていたデータが含まれます。全体的に大きな違いはないと思いますが、コードを読みやすくすることができます。


1
「最善の方法」についての意見には同意しませんが、良い代替案として+1します。
タイラーマック2009

このアプローチの問題は、すべての新しいオブジェクトに、膨大な量のデータになる可能性のあるものが含まれている(または参照されている)ことです。これらのgobを含むオブジェクトのvar_dump()は、再帰警告で自由にペパーリングされた巨大なリストを非常にすばやく生成します。それは醜く醜く、ひどく効率的であることができず、物事が混乱しているように見えます。しかし、私は個人的にもっと良い方法を見つけていません。「factory」メソッドを曲げて、__ construct()を使用してグローバルを参照しました。しかし、恐ろしいシングルトンを避けるために、すべてが後方に曲がっているように感じます...
FYA

2
@EastGhostCom:シングルトンを使用して、自分自身を困難にしようとするのをやめるかもしれません:)
gbjbaanb

5

ここで明白なことを述べているかもしれませんが、SpringGuiceなどの依存性注入フレームワークを使用できない理由はありますか?(Springは現在.NETでも利用できると思います)。

このようにして、フレームワークは構成オブジェクトの単一のコピーを保持でき、Bean(サービス、DAOなど)はそれを検索する必要がありません。

これが私がいつも取っているアプローチです!


4

Spring Frameworkを使用している場合は、通常のBeanを作成できます。デフォルトでは(または明示的に設定した場合scope="singleton")、Beanのインスタンスが1つだけ作成され、そのインスタンスは、Beanが依存関係で使用されるか、を介して取得されるたびに返されますgetBean()

シングルトンパターンを結合しなくても、単一インスタンスの利点が得られます。


3
Oh the皮肉-(シングルトン)Spring Beanを使用してシングルトンを置き換える...
Zack Macomber

4

別の方法は、オブジェクトに物事を尋ねるのではなく、必要なものを渡すことです。


4

理解が難しく、壊れやすい非常に大きなオブジェクトで終了するため、単一の構成オブジェクトに対する責任を積み重ねないでください

たとえば、特定のクラスに別のパラメータが必要な場合は、Configurationオブジェクトを変更し、それを使用するすべてのクラスを再コンパイルします。これはやや問題があります。

共通のグローバルで大きなConfigurationオブジェクトを回避するために、コードをリファクタリングしてみてください。必要なパラメーターのみをクライアントクラスに渡します。

class Server {

    int port;

    Server(Configuration config) {
        this.port = config.getServerPort();
    } 

}

次のようにリファクタリングする必要があります:

 class Server {

    public Server(int port) {
       this.port = port;
    }
 }

ここでは、依存性注入フレームワークが役立ちますが、厳密には必須ではありません。


はい、それは本当に良い点です。私は以前これをやったことがあります。私の大きな構成オブジェクトはMailServiceConf、ServerConfのようなインターフェイスを実装していました。依存性注入フレームワークが構成をクラスに渡すので、私のクラスは大きな構成オブジェクトに依存していませんでした。
caltuntas 2010年

1

静的メソッドを使用して、シングルトンと同じ動作を実現できます。Steve yeggeがこの投稿で非常によく説明しています。


実際、記事は非常に優れており、代わりに静的メソッドを使用する必要があるとは記載されていません。代わりに、彼は静的メソッドも単なるシングルトンであると述べ、最後にファクトリーメソッドパターンを使用することをお勧めします。 ...」
FrankS、2009

0

静的なメソッドとフィールドのみを含むクラスは可能ですか?私はあなたの状況が正確に何であるかわかりませんが、調査する価値があるかもしれません。


1
クラスがステートレスの場合、静的クラスである必要があります。
AlbertoPL 2009

1
それはC ++にあります-パターンはMonostateとして知られています。

0

使用されているツール/フレームワークなどによって異なります。依存関係注入/ iocツールを使用する場合、クラスのインスタンスを1つだけ作成するだけで、di / iocコンテナーが必要なクラス(IConfigSettingsインターフェイスなど)のシングルトン動作を使用することで、シングルトンパフォーマンス/最適化を得ることができます。これはまだテストに置き換えることができます

または、ファクトリを使用してクラスを作成し、リクエストするたびに同じインスタンスを返すこともできますが、テストの場合は、スタブされたバージョンまたはモックされたバージョンを返すことができます


0

コールバックインターフェイスとして構成する可能性を確認します。したがって、構成に依存するコードは次のようになります。

MyReuseCode.Configure(IConfiguration)

System-initコードは次のようになります。

Library.init(MyIConfigurationImpl)

0

依存関係注入フレームワークを使用して、構成オブジェクトを渡す手間を軽減できます。まともなものはninjectで、xmlではなくコードを使用するという利点があります。


0

あまりきれいではないかもしれませんが、変更したい情報ビットをシングルトンを作成するメソッドに渡すことができます-を使用する代わりに

public static Singleton getInstance() {
    if(singleton != null)
        createSingleton();
        return singleton;
    }
}

createSingleton(Information info)アプリケーションの起動時に(および単体テストのsetUp-Methodsで)直接呼び出すことができます。


-1

シングルトンは悪ではありませんが、デザインパターンに欠陥があります。実行時にそのインスタンスを1つだけ作成したいが、ユニットテスト中に複数の分離されたインスタンスを作成して確定的な結果を保証したいクラスがあります。

Springなどを使用したDIは非常に優れたオプションですが、唯一のオプションではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.