循環クラスの依存関係


12

互いに必要な2つのクラスがあるのは悪い設計ですか?

私はGameEngineいくつかのGameStateオブジェクトを持っているクラスを持っている小さなゲームを書いています。いくつかのレンダリングメソッドにアクセスするには、これらのGameStateオブジェクトもGameEngineクラスを知る必要があります。つまり、循環依存関係です。

この悪いデザインを呼んでもらえますか?よくわからないが、現時点ではこれらのことをリファクタリングできるので、私はただ尋ねている。


2
答えが「はい」の場合、私は非常に驚くでしょう。
doppelgreener

2つの質問があるので、それらは論理的にばらばらです。答えはそれらの1つに対してはいです。なぜ実際に何かをする状態を設計したいのですか?状態を確認してアクションを実行するマネージャー/コントローラーが必要です。オブジェクトの状態タイプが他の責任を引き継ぐのは少し混乱します。やりたいなら、C ++があなたの友人です。
テオドロン

私のGameStateクラスは、イントロ、実際のゲーム、メニューなどのようなものです。GameEngineはこれらの状態をスタックに保存するため、状態を一時停止してメニューを開いたり、カットシーンを再生したりできます... GameStateクラスはGameEngineを認識する必要があります。 。
shad0w

5
覚えておいて、これらのルールはすべて高速を目指しています。実行が高速、作成が高速、保守が高速。場合によっては、これらのファセットは互いに対立しているため、コストベネフィット分析を行って、処理方法を決定する必要があります。それだけでも考えて、ガッツ電話をしたとしても、開発者の90%を上回っています。あなたがそれを間違えた場合あなたを殺そうとしている隠されたモンスターは存在しません。彼はより多くの隠された料金所オペレーターです。
DampeS8N

回答:


0

それは本質的に悪い設計ではありませんが、簡単に制御できなくなる可能性があります。実際、あなたは日常のコードでそのデザインを使用しています。たとえば、ベクターは最初のイテレーターであり、イテレーターはコンテナーへのポインターを持っていることを知っています。

今、あなたの場合、GameEnigneとGameStateに2つの別々のクラスを用意する方がずっと良いです。基本的にこれら2つは異なることをしているので、後でGameState(ゲームの各シーンのGameStateなど)を継承する多くのクラスを定義できます。そして、お互いにアクセスする必要を否定することはできません。基本的にGameEngineはゲームステートを実行しているため、それらへのポインターが必要です。また、GameStateはGameEngineで定義されたリソース(レンダリング、物理マネージャーなど)を使用しています。

これらの2つのクラスを互いに結合することはできません。また、それらは本質的に異なることを行っており、結合すると誰も好まない非常に大きなクラスになります。

これまでのところ、アウトデザインには循環依存が必要であることを知っています。安全に作成する方法は複数あります。

  1. あなたが提案したように、それぞれのポインタを別のポインタに入れることができます。これは最も簡単な解決策ですが、簡単に制御できなくなる可能性があります。
  2. また、シングルトン/マルチトン設計を使用することもできます。このメソッドでは、GameEngineクラスをシングルトンとして定義し、それらのGameStatesをマルチトンとして定義する必要があります。多くの開発者はシングルトンとマルチトンの両方をAntiPatternだと考えていますが、私はこのようなデザインを好みます。
  3. グローバル変数を使用できます。基本的にはシングルトン/マルチトンと基本的に同じですが、プログラマーがインスタンスを自由に作成することを制限しないというわずかな違いがあります。

彼の答えを結論付けるには、これらの3つの方法のいずれかを使用できますが、これらの設計はすべて非常に危険であり、保守不能なコードになりやすいため、いずれかを使いすぎないように注意する必要があります。


6

互いに必要な2つのクラスがあるのは悪い設計ですか?

ちょっとしたコード臭ですが、そのままにしておくことができます。それがあなたのゲームを立ち上げて実行するためのより簡単でより速い方法であるならば、それのために行きなさい。ただし、ある時点でリファクタリングしなければならない可能性が十分にあるため、この点に留意してください。

C ++の問題は、循環依存関係はそれほど簡単はコンパイルされないためコンパイルの修正に時間を費やすのではなく循環依存関係を取り除くことをお勧めします。

さらにいくつかの意見については、SOに関するこの質問を参照してください。

[私のデザイン]を悪いデザインと呼びますか?

いいえ、すべてを1つのクラスに入れるよりはましです。

それほど素晴らしいものではありませんが、実際に私が見たほとんどの実装に非常に近いです。通常、ゲームの状態のマネージャークラス(注意してください!)とレンダラークラスがあり、それらはシングルトンであることが非常に一般的です。そのため、循環依存関係は「非表示」ですが、潜在的には存在します。

また、コメントで言われたように、ゲーム状態クラスが何らかのレンダリングを実行するのは少し奇妙です。状態情報を保持するだけで、レンダリングはレンダラーまたはゲームオブジェクト自体のグラフィックコンポーネントによって処理される必要があります。

、究極のデザインがあるかもしれません。他の答えが良いアイデアをもたらすかどうか知りたいです。それでも、あなたはおそらくあなたのゲームに最適なデザインを見つけることができる人です。


なぜコードの匂いがするのですか?親子関係を持つオブジェクトのほとんどは、お互いを見る必要があります。
喜界丸

4
なぜなら、どのクラスが何に対して責任があるのか​​を定義しない最も簡単な方法だからです。それは簡単に2つのクラスになり、それらはいずれも新しいコードを追加できるほど深く結合されているため、概念的に分離されなくなりました。また、スパゲッティコードのオープンドアでもあります。クラスAはこのためにクラスBを呼び出しますが、BはAからの情報などを必要とします。可能であれば、そうでない方が良いでしょう。
ローランクーヴィドゥ

それは滑りやすい斜面の誤fallです。静的クラスも悪いことにつながる可能性がありますが、utilクラスはコードの匂いではありません。また、SceneにはSceneNodeがあり、SceneNodeにはSceneへの参照があるので、2つの異なるシーンに追加することはできません。AはBを必要としますか、BはCを必要とし、CはAを必要としますか?
喜界丸

実際、これは静的クラスに似ています。注意して取り扱ってください。必要な場合にのみ使用してください。今、私は経験から話をしておりこのよう考えているのは私だけではありませんが、実際、これは意見の問題です。私の意見では、OPによって与えられたコンテキストでは、これは確かに臭いです。
ローランクーヴィドゥー

そのウィキペディアの記事にはそのケースに関する言及はありません。そして、それらのクラスを実際に見るまで、「不適切な親密さ」の場合とは言えません。
喜界丸

6

相互に直接参照する2つのクラスを持たなければならないことは、よくない設計と見なされます。実際には、コード全体の制御フローに従うのが難しくなり、オブジェクトの所有権とその存続期間が複雑になる可能性があります。つまり、どちらのクラスも他のクラスなしで再利用できないことを意味します。これらのクラスの3番目の「メディエーター」クラスなど。

ただし、2つのオブジェクトが相互に参照することは非常に一般的であり、ここでの違いは、通常、一方向の関係がより抽象的なことです。例えば、モデル-ビュー-コントローラのシステムでは、Viewオブジェクトは、ビューは、関連するデータに自身を移入することができるように、すべてのメソッドとプロパティにアクセスできること、モデルオブジェクトへの参照を保持することができる、そしてそれについてのすべてを知っているだろう。Modelオブジェクトは、自身のデータが変更されたときにViewを更新できるように、Viewへの参照を保持する場合があります。しかし、モデルがビューに依存するようにするビュー参照を持つモデルではなく、通常、ビューは多くの場合1つだけでObservableインターフェースを実装しますUpdate()関数、およびモデルは、ビューなどのObservableオブジェクトへの参照を保持します。モデルが変更されるとUpdate()、すべてのObservableが呼び出され、ビューUpdate()はモデルにコールバックして更新された情報を取得することで実装します。ここでの利点は、モデルがビューについてまったく何も知らないことで(そして、なぜそうすべきなのか)、ビューがない場合でも、他の状況で再利用できることです。

ゲームでも同様の状況があります。GameEngineは通常、GameStateについて認識します。ただし、GameStateはGameEngineについてすべてを知る必要はありません。GameEngineの特定のレンダリングメソッドにアクセスするだけです。これにより、(a)GameEngineが1つのクラス内であまりにも多くのことをしようとしている、または(b)GameStateがゲームエンジン全体を必要とせず、レンダリング可能な部分のみを必要とするという小さなアラームが頭に表示されます。

これを解決するための3つのアプローチは次のとおりです。

  • GameEngineをRenderableインターフェースから派生させ、その参照をGameStateに渡します。レンダリングパーツをリファクタリングする場合は、同じインターフェイスを維持する必要があります。
  • レンダリングパーツを新しいレンダラーオブジェクトに分解し、代わりにGameStatesに渡します。
  • そのままにしておきます。いつか、GameStateからGameEngineのすべての機能にアクセスしたいと思うかもしれません。ただし、保守が容易で拡張が容易なソフトウェアでは、通常、各クラスが外部で参照するものをできるだけ少なくする必要があることに留意してください。定義済みのタスクが望ましいです。

0

一般に、結合度が低く、凝集度が高いことをお勧めします。結合と結合に関するいくつかのリンクを次に示します。

ウィキペディアの結合

ウィキペディアの結束

低結合、高凝集

ベストプラクティスのstackoverflow

2つのクラスが相互に参照していることは、高い結合を持っていることです。GoogleのGuiceのフレームワークの依存性注入の手段によって低い結合で高い凝集性を達成することを目的とします。私はあなたがもう少し話題をよく読んでお勧めしてから、コンテキスト与えられた自分自身の呼び出しを行います。

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