複数のサブシステムを持つ Java スイング アプリケーションを開発しています。すべての意図と目的のために、ランダムな追加機能を備えたインターネット チャット プログラムを作成していると仮定しましょう。その機能は... 時間を設定してその時間にリマインダーを受け取ることができるスケジューラーであり、リマインダーを受け取ったことを友達リストの全員に通知することもできます。
この機能を GUI、ChatManager、および Scheduler の 3 つのクラスに編成することは理にかなっています。これらのクラスは次のことを行います。
GUI - すべての Swing コンポーネントとイベントを定義します
ChatManager - チャット接続の作成、メッセージの送受信、フレンド リストの管理
スケジューラ- システム時間の監視、通知の送信、セッション間のイベントを記憶するためのファイルの保存
プログラムが機能するには、これらの各クラスが他の 2 つのクラスと通信できる必要があります。GUI は、いつメッセージを送信するかを ChatManager に通知し、いつ監視を開始するかを Scheduler に通知する必要があります。ChatManager は、受信したメッセージを GUI に表示する必要があります。最後に、Scheduler は、完了時に GUI に通知し、ステータスの更新などを ChatManager に送信する必要があります。
もちろん、ここで説明するクラスはすべて非常に単純であり、相互に直接通信できるようにすることも悪くないかもしれません。ただし、この質問のために、相互作用がはるかに複雑であると仮定しましょう。
たとえば、特定の時刻ではなく、特定のイベントをスケジューラに登録できるとします。メッセージを送信するときは、メッセージをユーザーに送信し、ログ ファイルに保存し、イベント オブジェクトを作成してスケジューラに渡し、途中でスローされる可能性のある例外を処理しました。
通信がこれほど複雑になると、これらのクラスとの通信がさまざまな場所で発生する可能性がある場合、コードの保守が難しくなります。たとえば、ChatManager をリファクタリングする場合、GUI とスケジューラの両方に大幅な変更を加える必要があるかもしれません (何か新しいものを導入する場合は、それ以外にも)。これにより、コードの保守が難しくなり、睡眠不足のプログラマーが変更を加えるときにバグが発生する可能性が高くなります。
最初は最も理にかなっているように見えた解決策は、メディエーター デザイン パターンを使用することです。これら 3 つの主要なクラスは、互いに直接認識しておらず、代わりにそれぞれがメディエーター クラスを認識しているという考え方です。次に、メディエータ クラスは、3 つのクラス間の通信を処理するメソッドを定義します。したがって、たとえば、GUI はメディエーター クラスの sendMessage() メソッドを呼び出し、メディエーターは必要なすべての処理を行います。最終的に、これにより 3 つの主要なクラスが分離され、そのうちの 1 つを変更すると、メディエーターが変更されるだけになる可能性があります。
しかし、これには 2 つの主な問題があり、最終的にはフィードバックを求めてここに来ました。それらは次のとおりです。
問題
多くのタスクで GUI を更新する必要がありますが、メディエーターはコンポーネントを認識していません。- ユーザーがプログラムを起動し、ユーザー名とパスワードを入力し、[ログイン] をクリックしてチャット サーバーにログインするとします。ログイン中に、「接続中...」、「ログイン中...」、「エラー」などのテキストをログイン画面に表示して、ログインプロセスを報告したい。Mediator クラスでログイン メソッドを定義する場合、これらの通知を表示する唯一の方法は、正しい JLabel を更新するパブリック メソッドを GUI クラスで作成することです。最終的に、GUI クラスには、特定のユーザーからのメッセージの表示、ユーザーのログオン/オフ時のフレンド リストの更新など、そのコンポーネントを更新するための非常に大量のメソッドが必要になります。また、これらの GUI の更新はいつでもランダムに発生する可能性があることを予期する必要があります。それは大丈夫ですか?
Swing イベント ディスパッチ スレッド。ほとんどの場合、EDT で実行されるコンポーネント ActionListeners からメディエーター メソッドを呼び出します。ただし、EDT でメッセージを送信したり、ファイルを読み書きしたりしたくない場合は、GUI が応答しなくなります。したがって、SingleThreadExecutor をメディエーター オブジェクトで使用可能にし、メディエーター オブジェクトのすべてのメソッドで、エグゼキューター スレッドに送信できる新しい実行可能ファイルを定義することは良い考えでしょうか? また、GUI コンポーネントの更新は EDT で行う必要がありますが、その Executor スレッドはメソッドを呼び出して GUI コンポーネントを更新します。したがって、GUI クラスのすべてのパブリック メソッドは、実行のために EDT に送信する必要があります。それは必要ですか?
私には、外部と通信するすべてのコンポーネントを更新するためのメソッドを GUI クラスに用意するのは大変な作業のように思えます。これらの各メソッドには、EDT 上にあるかどうかをチェックし、自分自身を追加する追加のオーバーヘッドがあります。それ以外の場合は EDT。さらに、Mediator クラスのすべての public メソッドは、Runnable コードを Mediator スレッドに追加するか、ワーカー スレッドを起動するなど、同様のことを行う必要があります。
全体として、Mediator パターンを使用してアプリケーションを維持することは、それを使用せずにアプリケーションを維持することとほぼ同じくらいの作業のように思えます。では、この例では、何か違うとしたら何をしますか?