3

私のプロジェクトは、JavaのSwingライブラリに基づいて構築されています。GUIを表示するEDTを生成します(正しく機能します)。

EDTを初期化するプログラムへの入り口:

public final class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Start());
    }

    class Start implements Runnable {
        private Model model = new Model();
        private Controller controller = new Controller(model);
        private View view = new View(controller);

        @Override
        public void run() {
            // Initialize the view and display its JFrame...
        }
    }
}

}

ただし、GUI内でボタン/ラジオボックスなどをクリックすると、Controllerクラスがモデルに対してアクションを実行する必要があります。

私の質問は次のとおりです。

  • コントローラのコードを新しいSwingWorkerでラップする必要がありますか?
    • いいえの場合、モデルのコードを新しいSwingWorkerでラップする必要がありますか?
  • コントローラのコードをスレッドでラップする場合、モデル内の共有状態変数を同期する必要がありますか?
  • 新しいスレッドで実行されているモデルがGUIに変更を通知した場合、これはEDTまたは新しいスレッドで発生しますか?

例えば:

public class Controller {
    public void updateModel() {
        new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() throws Exception {
                model.somethingSomethingSomething();
            }
        }.execute();
    }
}

public class Model {
    public void somethingSomethingSomething() {
        notifyListeners(); // This is going to notify whichever GUI 
                           // is listening to the model.
                           // Does it have to be wrapped with Swing.invokeLater?
    }
}

public class View {
    // This function is called when the model notifies its listeners.
    public void modelChangedNotifier() {
        button.setText("THE MODEL HAS CHANGED"); // Does this occur on the EDT?
    }
}
4

3 に答える 3

4

ここでそれについて読むことができます:JavaSE6のSwingWorkerでアプリケーションのパフォーマンスを改善します。つまり、UIに影響されない、時間のかかるすべての操作は、別のスレッドで実行する必要があります。操作の結果を表示するには、EDTに戻る必要があります。たとえば、データベース検索を行う場合は、プログレスバー(通常は無限)を表示し、SwingWorkerを使用して検索を開始する必要があります。検索結果をテーブルに表示するには、EDTに参加している必要があります。または、 foxtrotlibを使用することもできます(コードを再設計せずに、コードをより便利なSwingにすることができます)。コントローラコードがswingウィジェットを永続的に更新する場合は、EDTで実行するか、少なくともEDTでUIのこれらの更新を実行する必要があります(SwingUtilities.invokeLater、SwingWorkerまたはswing.Timerでのチャンク処理を使用)。したがって、サンプルは間違っています。モデルの更新はEDTで最新である必要があります。

于 2013-02-05T21:44:59.530 に答える
4

からモデルを更新する代わりにdoInBackground()publish()中間結果とからモデルを更新しますprocess()。これはEDTで実行されます。このでは、はあなたに対応しJTable、はあなたViewTableModel対応しますModelJTable自身をリッスンすることに注意してくださいTableModel

于 2013-02-06T01:12:50.550 に答える
-2

実践9.4.2のJava同時実行からの1つの代替アプローチは、「分割」または「共有データモデル」を使用します。必要なスレッド、おそらく長時間実行される非EDTスレッドでビジネスモデルを更新します。ただし、notifyListeners()を直接呼び出して、現在のスレッドを気にする代わりに、myComponent.repaint()を呼び出すだけで、EDTで再描画要求がキューに入れられます。

次に、paintComponent()メソッドのどこかで、通常は次のメソッドで、モデルからすべての新しいデータを明示的に取得します。modelToView()

   commentTextArea.setText(myModel.getCommentText());
   fooLabel.setText(myModel.getFooText());
   ...

利点は、スレッド化が問題ではないことです。少なくとも一部の人にとっては、これは「理にかなっている」ことであり、モデルはビューから適切に分離されています。欠点は、毎回すべての値をリセットすることです。したがって、100個のJComponentがある場合、100個の設定が行われます。また、ビューはモデルとかなり緊密に結合されています。


作業コードの例

@MadProgrammerと@kleopatraは正しいので、ビューに更新中のコンポーネントが直接含まれていると、「運命の無限ループ」が発生します。証拠については、を参照してください

Demo_14716901_Fails

ただし、ビューがコンポーネントから分離されている場合は、無限ループを回避できます。通常、高レベルのビューには、JSplitPanes、JScrollPanesの保持、Box以上のJPanelsの保持、実際の低レベルのコンポーネントの保持などが含まれます。したがって、この要件であるIMOは不合理ではありません。

Demo_14716901_Worksでの作業コード

反対派のために追加されたいくつかのコメント

Swingを倒したい人もいます。彼らは機器制御コードまたはアルゴリズムを作成しており、EDT、無限のSwingWorkers、invokeLatersを気にせずに仕事を終わらせたいと思っています。このテクニックにより、彼らは仕事を成し遂げることができます。重要な注意点が1つありますが、これは機能します。(個人的に、私はSwingを理解し、一般的に好きですが、多くはそうではありません)。

SwingコンポーネントはMVCですが、一般的には非常にマイクロレベルです。「実際の」モデルは単一の文字列ではなく、数十の値です。「実際の」ビューは単一のJLabelではなく、多くのJPanelであり、それぞれに多くのコンポーネントがあり、スクローラー、スプリッターなどと組み合わされています。この手法は通常、現実の世界によりよく適合し、プログラマーがより高いレベルで自然に考えることができます。

「悪い習慣」に関しては、ブライアン・ゲッツ、ジョシュ・ブロックなどに取り上げてください。それは「権威に訴える」ことですが、私にとってはうまくいきます。:-)

于 2013-02-05T23:28:20.083 に答える