互いに必要な2つのクラスがあるのは悪い設計ですか?
私はGameEngine
いくつかのGameState
オブジェクトを持っているクラスを持っている小さなゲームを書いています。いくつかのレンダリングメソッドにアクセスするには、これらのGameState
オブジェクトもGameEngine
クラスを知る必要があります。つまり、循環依存関係です。
この悪いデザインを呼んでもらえますか?よくわからないが、現時点ではこれらのことをリファクタリングできるので、私はただ尋ねている。
互いに必要な2つのクラスがあるのは悪い設計ですか?
私はGameEngine
いくつかのGameState
オブジェクトを持っているクラスを持っている小さなゲームを書いています。いくつかのレンダリングメソッドにアクセスするには、これらのGameState
オブジェクトもGameEngine
クラスを知る必要があります。つまり、循環依存関係です。
この悪いデザインを呼んでもらえますか?よくわからないが、現時点ではこれらのことをリファクタリングできるので、私はただ尋ねている。
回答:
それは本質的に悪い設計ではありませんが、簡単に制御できなくなる可能性があります。実際、あなたは日常のコードでそのデザインを使用しています。たとえば、ベクターは最初のイテレーターであり、イテレーターはコンテナーへのポインターを持っていることを知っています。
今、あなたの場合、GameEnigneとGameStateに2つの別々のクラスを用意する方がずっと良いです。基本的にこれら2つは異なることをしているので、後でGameState(ゲームの各シーンのGameStateなど)を継承する多くのクラスを定義できます。そして、お互いにアクセスする必要を否定することはできません。基本的にGameEngineはゲームステートを実行しているため、それらへのポインターが必要です。また、GameStateはGameEngineで定義されたリソース(レンダリング、物理マネージャーなど)を使用しています。
これらの2つのクラスを互いに結合することはできません。また、それらは本質的に異なることを行っており、結合すると誰も好まない非常に大きなクラスになります。
これまでのところ、アウトデザインには循環依存が必要であることを知っています。安全に作成する方法は複数あります。
彼の答えを結論付けるには、これらの3つの方法のいずれかを使用できますが、これらの設計はすべて非常に危険であり、保守不能なコードになりやすいため、いずれかを使いすぎないように注意する必要があります。
互いに必要な2つのクラスがあるのは悪い設計ですか?
ちょっとしたコード臭ですが、そのままにしておくことができます。それがあなたのゲームを立ち上げて実行するためのより簡単でより速い方法であるならば、それのために行きなさい。ただし、ある時点でリファクタリングしなければならない可能性が十分にあるため、この点に留意してください。
C ++の問題は、循環依存関係はそれほど簡単にはコンパイルされないため、コンパイルの修正に時間を費やすのではなく、循環依存関係を取り除くことをお勧めします。
さらにいくつかの意見については、SOに関するこの質問を参照してください。
[私のデザイン]を悪いデザインと呼びますか?
いいえ、すべてを1つのクラスに入れるよりはましです。
それほど素晴らしいものではありませんが、実際に私が見たほとんどの実装に非常に近いです。通常、ゲームの状態のマネージャークラス(注意してください!)とレンダラークラスがあり、それらはシングルトンであることが非常に一般的です。そのため、循環依存関係は「非表示」ですが、潜在的には存在します。
また、コメントで言われたように、ゲーム状態クラスが何らかのレンダリングを実行するのは少し奇妙です。状態情報を保持するだけで、レンダリングはレンダラーまたはゲームオブジェクト自体のグラフィックコンポーネントによって処理される必要があります。
今、究極のデザインがあるかもしれません。他の答えが良いアイデアをもたらすかどうか知りたいです。それでも、あなたはおそらくあなたのゲームに最適なデザインを見つけることができる人です。
相互に直接参照する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つのアプローチは次のとおりです。
一般に、結合度が低く、凝集度が高いことをお勧めします。結合と結合に関するいくつかのリンクを次に示します。
2つのクラスが相互に参照していることは、高い結合を持っていることです。GoogleのGuiceのフレームワークの依存性注入の手段によって低い結合で高い凝集性を達成することを目的とします。私はあなたがもう少し話題をよく読んでお勧めしてから、コンテキスト与えられた自分自身の呼び出しを行います。