私はプログラマーに不慣れで、プログラミングの知識を増やしたいと思っています。最近、Stack Overflowのユーザーから、シングルトンを使用するのは悪い考えだと言われました。シングルトンは密結合を促進し、テストを難しくしているからです。
この議論のきっかけとなったのは、私が書いたすべてのクラスが、シングルトンを介してあらゆる種類のデータにアクセスしたことです。たとえば、ゲームの状態データを処理するクラス。他のクラスがこのデータにアクセスしたい場合、シングルトンインスタンスを取得するだけで済みます。具体的には、AppDelegateでクラスをインスタンス化し、[[[[UIApplicationsharedApplication]デリゲート] gameObject] getScore]を介してクラスにアクセスします。
SOユーザーは、「gameObject」を最初に必要な場所でインスタンス化し、そのオブジェクトを、引数としてのメソッドまたはプロパティ。
この情報が完全に間違っていること、シングルトンは結合とは無関係であり、テストにはまったく影響しないことを他の誰かが指摘したときに、これらの点について話し合っていました。
私は今、どちらの人が正しいかについて完全に混乱しています。誰かが、オブジェクトがアプリの存続期間中持続し、他のクラスにアクセスでき、疎結合を維持するのに最適な手法を明確にしてください。私には、メソッドまたはプロパティによって各クラスに渡されるオブジェクトが最も論理的であるように見えますが、わかりません。
助けてくれてありがとう!
あなたは決して知っている。プログラムの設計はトレードオフに関するものであり、特定のシナリオを分析して、それらについて知っていることを考えると、良い(最善ではない)ソリューションを生成します。この特定のシナリオでは、シングルトンの提案者は明らかに間違っていますが、要点を見逃しています。
答えがあまり明確でないシナリオでは、目前の問題に対する良いアプローチと悪いアプローチを判断するために、自分で物事を考えることができる必要があります。カップリングとは何か、シングルトンとは何かを知っている場合は、実際に私たちに尋ねる必要はありません。シナリオの詳細があなたに答えを得ることができないことを意味するとき、それらのためにこのスキルを開発することは重要です。
結合を作成する部分は、変数がシングルトンに存在するということではなく、クライアントオブジェクトがそこから移動してフェッチすることです。つまり、あらゆる種類の手間をかけずにオブジェクトの保存方法を変更することはできません(あなたが言うように、多くのオブジェクトがこのように動作すると仮定します)。
たとえば、プログラムをMDI(マルチドキュメントインターフェイス)にして、ユーザーが一度に複数のゲームをプレイできるようにしたい)と決めた場合、どうなるか考えてください。プログラム全体がアクセスするので、このシングルトン、あなたはほとんど自分自身を台無しにしました。
一方、ゲームをシングルトンに格納して、アクセスする必要のあるオブジェクトに注入することができます。依存性注入を調べます。その時点でなぜシングルトンになるのかはわかりませんが、完全に無意味ではないかもしれません。
基本的に、それらは両方とも正しいです。
シングルトンに関して、シングルトンを使用するクラスをテストしたいが、それをモックしたい場合を考えてみましょう。 canは、getSingleton
というメソッドを抽出し、それをテスト可能なサブクラスでオーバーライドすることで実行できますが、この方法では面倒になります。
コンストラクター内の他のクラスへの依存関係を受け入れる場合、クラスはよりテスト可能になります。次に、テストにモック依存関係を挿入できます。
これをプロジェクト全体に適用すると、シングルトンはもう必要ないことがわかりますが、基本的にはメインメソッドからプロジェクトを「配線」する必要があります。
Guiceはこれを支援するDIフレームワークですが、最初に手動で操作することをお勧めします。そうすれば、概念を学ぶことができます。
あなたがもっと学びたいのなら、私は本当にMiškoHeveryが言わなければならないことをお勧めします。
ビデオ:
ブログ:
一日の終わりに、あなたはあなたが最も理にかなっていることをしなければなりません。シングルトンで結構です。それらがあなたのために働いたなら、それらを使ってください。
それらの問題は、いつか複数のそれらが必要になるかもしれないということです。ゲームプログラムは、2つ以上のゲームを同時にジャグリングしたい場合があります。次に、GameObjectへのすべての参照を追跡し、より複雑なものに置き換えます。最初から複数のgameObject
sを許可するコストは、後で機能を改良するよりもはるかに少なく、プログラムの全期間にわたってシングルトンのゲームオブジェクトを使用するよりもわずかに高くなります。
複数のゲームがないことが確実な場合は、シングルトンを使用する方が安価です。 (それらを回避するとプログラミングスキルが向上する可能性がありますが、シングルトンを使用できない場合には良い習慣かもしれません。)個人的には、コード内のシングルトンを取り除くのに十分な時間を費やしたので、今は回避します。とにかく。
あなたの場合、シングルトンはカップリングに関連しておらず、テストにはまったく影響しません(それらを削除した後に行う必要がある完全な再テストを除く)。
ただし、コードがWeb HTTPリクエストに応答し、したがって数百のスレッドで同時に実行され、シングルトンにアクセスする場合(さらに、any複数のスレッドに表示されるデータ)は非常に注意が必要です。マルチスレッドプログラミングをよく理解していないと、大きな列車事故が発生します。シングルトンの値はいつでも変更されます。バグはいくつかのスレッドの正確なタイミングに依存するため、再現できません。信頼性の高いテストは不可能になります。
この種のプログラミングをしている人はたくさんいます。彼らはマルチスレッドを理解していません。そして、後片付けをしなければならない人々は、シングルトンを使用すべきではないと強く感じています。これは、SOのユーザーが話していることです。(Ithink彼は新しいgameObject
を作成するようにアドバイスしていました個々のHTTPリクエストごとに。)これがあなたに関係があるとは思えませんが、誰もが何について話しているのかを知るのに役立ちます。
シングルトンは、名前空間を持つグローバル変数に非常によく似ています。ほとんどすべての人がグローバル変数は悪いと言っていますが、どういうわけか、シングルトンは大丈夫です。
そうは言っても、シングルトンにはいくつかの特定の用途があります。 one何かのインスタンスが確実に必要なときに使用できます。たとえば、10個のデータベーススレッドのみが必要です。そうしないと、負荷がかかるとデータベースサーバーがクラッシュします。その場合、ThreadPool
クラスはシングルトンであり、DBスレッドプールを管理する必要があります。
もう1つの使用法は、本質的にグローバル変数である読み取り専用(またはほとんど変更されない)構成がある場合です。この場合、メモリと時折ファイルIOを節約して、シングルトンとして使用します。
あなたの例では、GameStateをシングルトンとして使用したいとします。なぜプログラムを1つのゲームに制限するのですか?最初に1つのゲームが必要だったとしても、GameStateクラスにシングルトンロジックを配置することで自分自身を制限しています。
アプリケーションがわかりませんが、ゲームオブジェクトを個別に作成します。
_void startGame() {
Game gs = new Game();
gs.play();
}
_
では、どのようにしてスコアを見つけますか?誰がスコアを必要としているかによります。 Game
クラスがスコアを所有していると仮定すると、getScore()
関数を持つことができます。 Player
にスコアが必要な場合があります。これは、プレーヤーにGame
参照を持たせることで解決できます。
_void startGame(Player[] players) {
Game gs = new Game();
for ( Player player : players ) {
player.registerForGame(this);
}
gs.addPlayers(players);
gs.play();
}
Player::registerForGame(Game game) {
this.currentGame = game;
}
_
コードは特定の言語ではなく、シングルトンなしでどのように設計できるかを示したものにすぎません。また、これが「優れた」設計であるとは主張しません。たとえば、ゲームに多くの機能がある場合、Game
クラスはおそらくここでは多すぎます。
誰かが言ったように、デザインは多くのトレードオフであり、特定の問題に対して意味のあることをしなければなりません。
コードを再利用する場合(たとえば、マルチゲームサーバーを作成する場合)、または継続的な開発やチーム開発のための単体テストを作成する場合は、デカップリングに価値があります。
ただし、コードを再利用しない場合や、コードをさらにテストする必要がある場合は、コードを分離するための時間と労力が完全に無駄になる可能性があります(たとえば、ゲームは機能しますが、売れ行きが悪いため、他のプロジェクトに進みます)。さらに、Compute Engineの最も内側のループ(物理シミュレーションなど)でのデカップリングが多すぎると、実際にはユーザーのバッテリー寿命が無駄になる可能性があります(たとえば、「グリーン」ではない)。
ゲームが寝台車で、来年は売れ行きが良く、アプリを更新するにはコードに戻って読み取る必要がある場合、デカップリングの付加価値が発揮される可能性があります。次に、可読性、潜在的なコードの再利用、およびテスト容易性を追加するための分離は、結局のところ価値を持つことになります。
それはトレードオフとギャンブルです。多くの大規模なミッションクリティカルなプロジェクトは、デカップリングが付加価値をもたらすことを賭けなければなりません(そしてほとんどの場合その賭けに勝ちます)。アプリストアで死ぬ多くの小さなアプリにとって、ギャンブルは完全に失われる可能性があります。
さて、singletonsから始めましょう。
私はJS /クライアントサイドのWeb開発者で、最近シングルトンを実装することを決定しました。私は、ページ上の特定のタイプのすべてのUI要素を表すという点でややモノリシックなUIオブジェクトを作成しています。 UIオブジェクトは、ファクトリ、ウェアハウス、およびイベント/インターフェイスマネージャです。
同じタイプを表すオブジェクトから特定のタイプのこれらのUI要素のすべてのインスタンスを構築および管理する際のポイントは、UIで一度に1つのUI要素を処理したい場合と、すべてのUI要素に応答したい場合があることです。同じイベント。また、よりイベント駆動型のインターフェースを簡単に作成できるようになります。これは、最近私が探していたものです。
しかし、同じコンストラクターから複数のファクトリを許可する場合、たとえば、各一般的なUIタイプのオブジェクトインスタンスは、グローバルイベントですべてのUI要素をヒットしようとすると、厄介な、または少なくとも不器用な時間が発生します。実際に構築したUI要素への参照があります。代わりに、私の一般的なUIタイプのオブジェクトファクトリメソッドは、元のオブジェクトがすでに作成されている場合、それらへの参照を返すだけです。 1つのクラス/コンストラクター。 1つのインスタンス。
ショートバージョン:
シングルトンとは、複数のインスタンス化があると問題が発生する可能性がある場合に、複数のインスタンス化を防ぐことです。終わり。
シングルトンから離れて、密結合の主題:
密結合は通常、過剰な依存関係に関するものです。その情報がどこから来たのかを確認する手段なしにあまりにも多くの状態情報を含んでいる1つの大きな太ったグローバルオブジェクトは問題があるかもしれませんが、それは密結合ではありません。物事が密結合しすぎている場合は、環境の1つの関数、オブジェクト、またはランダム要素(Web上のHTMLクラスなど)に触れて、すべてが壊れるのを待ちます。
同じオブジェクトへの参照を過度に、奇妙な/ランダムな方法で渡すと、緊密に結合されたシナリオになる可能性がありますが、値によって渡され、それを利用している変数/プロパティからの参照を知っている場合は、ほとんどの場合良い考えです賢明な知識。