この再帰は、より一般的な設計上の問題の兆候です。確かに、コンストラクター内からインスタンス参照を「リーク」するか、後で呼び出す初期化メソッドに「構築」を延期することで解決を試みることができますが (おそらく遅延)、これはお勧めしません1。
GUIとPlayer クラスがシングルトンである必要があり、お互いを認識しなければならないのはなぜですか? あるオブジェクトにグローバルにアクセスできる方が「便利だ」以外に理由はありますか? 利便性が重要ではないというわけではありませんが、通常、設計を決定する唯一の理由にはなりません。
Player と GUI の 2 つのインスタンスが同時に存在できないという本当の理由はないため、シングルトンをすべて削除することをお勧めします。もちろん、プログラムにはそれぞれ 1 つしかないでしょうが、それはシングルトンの目的ではありません。
また、依存関係は一方向のみにする必要があります。他者なしに存在できるものと存在できないものについて考えてみてください。おそらく GUI のない Player を想像できるので、GUI は Player に依存するべきだと思いますが、Player のない GUI はまったく意味がありません。したがって、最初に Player を構築し、次に GUI を構築して、それが動作する Player インスタンスに渡します。Player 内のどこからでも GUI インスタンスにアクセスする必要があると思われる場合は、設計を再考し、それらのオブジェクトをより独立させるようにしてください。これは通常、Observer パターンを使用して行うことができ、GUI に変更/アクション/発生したものをリッスンさせます。この方法では、アプリケーション コードは GUI についてまったく認識しません。ただし、GUI 内では、GUI のインスタンスを好きなだけ渡すことができます。
'Manager' オブジェクトまたは Factory パターンを導入するというアイデアについて: それは役に立つかもしれませんが、それがなくても上で述べたことをすべて行うことができ、それでもまともな設計を維持できます。「マネージャー」クラスはあまりにも多くのことを行う傾向があり、通常は変装したシングルトンのようなものであるため、私は通常、「マネージャー」クラスに反対します。ただし、ファクトリは便利な場合がありますが、それは、たとえば、さまざまな種類の GUI を後で実装できるようにするなど、実際の構造をその使用法から切り離したい場合に限られます。
したがって、単純な実装は次のようになります。
public static void main(...) {
Player player = new Player();
// maybe some other stuff to configure/set up the player
GUI gui = new GUI(player);
gui.show(); // or something similar
}
ファクトリを使用したい場合は、もちろんnew ...
式を、以前に取得する必要があったファクトリへの呼び出しに置き換えることができます。
1 なぜ?たとえば、GUI の構築に成功したものの、後で初期化に失敗した場合 (例: 例外がスローされた場合) はどうなるでしょうか? この「無効な」状態を、既に GUI インスタンスを取得しているすべてのオブジェクトに伝える何らかの方法が必要です。また、GUI インスタンスを初期化する前に誰も使用しないようにする必要があります。