2

複数のサブシステムを持つ 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 つの主な問題があり、最終的にはフィードバックを求めてここに来ました。それらは次のとおりです。

問題

  1. 多くのタスクで GUI を更新する必要がありますが、メディエーターはコンポーネントを認識していません。- ユーザーがプログラムを起動し、ユーザー名とパスワードを入力し、[ログイン] をクリックしてチャット サーバーにログインするとします。ログイン中に、「接続中...」、「ログイン中...」、「エラー」などのテキストをログイン画面に表示して、ログインプロセスを報告したい。Mediator クラスでログイン メソッドを定義する場合、これらの通知を表示する唯一の方法は、正しい JLabel を更新するパブリック メソッドを GUI クラスで作成することです。最終的に、GUI クラスには、特定のユーザーからのメッセージの表示、ユーザーのログオン/オフ時のフレンド リストの更新など、そのコンポーネントを更新するための非常に大量のメソッドが必要になります。また、これらの GUI の更新はいつでもランダムに発生する可能性があることを予期する必要があります。それは大丈夫ですか?

  2. Swing イベント ディスパッチ スレッド。ほとんどの場合、EDT で実行されるコンポーネント ActionListeners からメディエーター メソッドを呼び出します。ただし、EDT でメッセージを送信したり、ファイルを読み書きしたりしたくない場合は、GUI が応答しなくなります。したがって、SingleThreadExecutor をメディエーター オブジェクトで使用可能にし、メディエーター オブジェクトのすべてのメソッドで、エグゼキューター スレッドに送信できる新しい実行可能ファイルを定義することは良い考えでしょうか? また、GUI コンポーネントの更新は EDT で行う必要がありますが、その Executor スレッドはメソッドを呼び出して GUI コンポーネントを更新します。したがって、GUI クラスのすべてのパブリック メソッドは、実行のために EDT に送信する必要があります。それは必要ですか?

私には、外部と通信するすべてのコンポーネントを更新するためのメソッドを GUI クラスに用意するのは大変な作業のように思えます。これらの各メソッドには、EDT 上にあるかどうかをチェックし、自分自身を追加する追加のオーバーヘッドがあります。それ以外の場合は EDT。さらに、Mediator クラスのすべての public メソッドは、Runnable コードを Mediator スレッドに追加するか、ワーカー スレッドを起動するなど、同様のことを行う必要があります。

全体として、Mediator パターンを使用してアプリケーションを維持することは、それを使用せずにアプリケーションを維持することとほぼ同じくらいの作業のように思えます。では、この例では、何か違うとしたら何をしますか?

4

4 に答える 4

3
  1. GUI クラスには、最新の状態に保つための多くのメソッドが含まれますが、それで問題ありません。心配な場合は、GUI をサブ GUI に分割して、それぞれが異なる機能または関連する機能の小さなセットを使用するオプションが常にあります。メソッドの数は明らかに変わりませんが、より組織化され、一貫性があり、分離されます。

  2. GUI のすべてのメソッドで Runnable を作成し、SwingUtilities.invokeLater を使用してその更新を EDT に適用する代わりに、別のソリューションを試すことをお勧めします。私の個人的なプロジェクトでは、Swing Application Framework (JSR296) を使用します。これには、バックグラウンド ジョブを起動するための便利なTaskクラスがいくつかあり、success メソッドは EDT スレッドで自動的に行われます。これを使用できない場合は、バックグラウンド ジョブ用に独自の同様のフレームワークを作成してみてください。

于 2009-06-09T08:52:34.847 に答える
1

ここで、設計に関する質問への部分的な回答...

コンポーネント間の結合を緩めたいようです。あなたの場合、私はメディエーターをGUIへのメッセージディスパッチャーとして使用します。

ChatManagerとスケジューラはUpdateUIMessageを生成します。

そして私は自分のGUIをそのように書くでしょう

public class MyView {

    public void handleUpdateMessage(final UpdateUIMessage msg){
        Runnable doRun = new Runnable(){
            public void run(){
                handleMessageOnEventDispatcherThread(msg);
            }
        };
        if(SwingUtilities.isEventDispatcherThread()){
            doRun.run();
        } else {
            SwingUtilities.invokeLater(doRun);
        }
    }
}

したがって、GUIにはすべてのEdTのものを処理するパブリックメソッドが1つだけあります。

GUIと他のコンポーネント間の結合を緩めたい場合(つまり、GUIに他のコンポーネントのすべてのAPIを認識させたくない場合)、GuiControllerはActionMessageを(特定のスレッドで?)公開することもできます。メディエーターによって他のコンポーネントにディスパッチされます。

それが役に立てば幸い。

于 2009-06-09T08:38:50.787 に答える
1

さて、私はあなたが働いている世界を変えます。3つのクラスがあり、それぞれがチャットの世界を観察しているだけです。MVC _あなたの問題に対処する方法です。あなたはあなたの世界のためのモデル、この場合はチャットプログラムを作成しなければなりませんでした。このモデルは、データ、チャットキュー、友達リストを保存し、一貫性を監視し、変更について関心のあるすべての人に通知します。また、世界の状態に関心があり、その状態をユーザーとサーバーに反映しているオブザーバーが何人かいます。GUIは、友達リストとメッセージキューに視覚化をもたらし、それらの変更に反応します。スケジューラは、スケジュールされたタスクの変更を調べ、その結果でモデルを更新します。ChatManagerは、SessionManager、MessageDispatcher、MessageAcceptorなどのいくつかのクラスでより適切に機能します。中央が空の3つのクラスがあります。センターを作成し、このセンターとオブザーバーパターンを使用してそれらを接続します。次に、各クラスは1つのクラスのみを扱い、興味深いイベントのみを扱います。1つのGUIクラスは悪い考えです。論理グループ(モデルのビュー)を表すより多くのサブクラスに分割します。これは、UIを征服する方法です。

于 2009-06-09T14:37:23.073 に答える
1

Flex 開発用の MVC フレームワークとして最初に開始されたプロジェクトを見てみることをお勧めします。その間、 PureMVCは Java を含む多くのプログラミング言語に移植されました。これを書いている時点ではアルファ版の状態ですが!

于 2009-06-09T15:35:51.320 に答える