web-dev-qa-db-ja.com

純粋に記述されたJavaFXでMVC設計パターンを実装する方法Java

現在、純粋なJava(Fxmlなしの意味))で記述されたJavaFXでバックアップアプリケーションを設計しています。

次の理由により、MVCパターンの実装に問題があります。私が理解している方法は、ビューはイベントを処理し、イベントが引き起こす変更を反映するようにモデルを更新するコントローラーから分離する必要があるということです。私の問題は、イベントをコントローラークラスにリンクすることです。これは、コンポーネントを宣言するビュークラスにイベントを追加する必要があるためです。これは、ビュークラスのコントローラーの一部を構成します。これは、私が理解している限り、MVCパターンが防止するはずのものです。

だから私の質問は、ビュークラスにコントローラーの一部を入れずに、ビュークラスからコントローラークラスにイベントをリンクする方法ですか?

4
SteelToe

これは少し物議をかもすかもしれませんが、個別のControllerクラスを使用して厳密なMVC分離を実装することは、JavaFXデスクトップアプリケーションを設計する最良の方法ではない可能性があるという結論に達しました。

私がそれに取り組んだ方法は、ほとんどの「コントローラー」アスペクトが組み込まれたリッチドメインモデルを構築することです。アプリケーションは、モデル、ビュー、UIコンポーネントクラスに階層化されています。

モデルクラスはドメインオブジェクトです。ビジネスまたはアプリケーションドメインをモデル化します。これらは、モデルを変更するためのドメイン固有のメソッドを実装します(子アイテムの追加、ステータスコードの変更など)。これは、ドメイン固有のコントローラー機能と考えることができます。

UIコンポーネントクラスは、Nodeを拡張またはカプセル化し、ドメインオブジェクトを受け入れ、その表示方法を理解するシーングラフオブジェクトです。また、モデルが自動的に更新されるように、一方向または双方向のバインディングを提供する場合もあります。表示を更新し、UIで編集するとモデルが自動的に更新されます。

Viewクラスは、UIの「タブ」または「ウィンドウ」の主要な側面を実装します。 UIコンポーネントのコレクションを保持し、グループまたはサブグループに配置します。ビューは、操作するドメインオブジェクトを受け入れ、必要なすべてのUIコンポーネントをバインド/配線する役割を果たします。また、ドメインモデルの認識が制限されている場合もあります。たとえば、モデルの状態に基づいて一部のUIコンポーネントを表示または非表示にする必要がある場合があります。したがって、これはUIプレゼンテーションに関連するコントローラー機能を処理します。

メニューコマンド、ショートカットキー、およびコンテキストメニューは、発行できるコマンドを表す「アクション」クラスにバインドされます。これらは、ビューを検査して現在のコンテキストと使用されているドメインオブジェクトを判別し、モデルにコマンドを発行できる機能の別のレイヤーです。

例として:-

顧客の注文をモデル化する「販売注文」オブジェクトがあります。 UIは、割引率を選択するためのドロップダウンを公開します。そのドロップダウンは、販売注文オブジェクトの「selectedDiscount」プロパティにバインドされています。モデル内には、そのプロパティが変更されたときに呼び出されるリスナーがあり、注文価格の再計算をトリガーします。

UIには注文ラインにバインドされたテーブルがあり、フィールドは販売注文のライン価格プロパティを監視します。ユーザーがドロップダウンを変更すると、価格の再計算がトリガーされ、モデルが更新されて、表示される価格が更新されます。

代替設計はたくさんありますが、特定のアプリケーションによっては、他の設計よりも適合する場合があります。

私は現在、かなり大規模で複雑なJavaFXデスクトップアプリケーションの開発に2年をかけています。現在、UIには500のクラスと数十のタブ/ウィンドウ/ダイアログがあり、これは私の個人的な経験に基づいています。私はいくつかの設計を実験しました-集中型コントローラーを備えたイベントバスベース;反応スタイル;プレゼンテーションモデルクラスを使用すると、さまざまな長所と短所があります。上記で概説したアプローチは、これまでのところうまくいきましたが、実際には、ここで示した非常に簡略化した説明よりも少し複雑です。これは、実用化され、重複を回避し、リファクタリングを容易にするのに十分な抽象化があり、邪魔になるほどの抽象化オーバーヘッドを追加しない、私が解決した最も単純なアプローチです。

残念ながら、JavaFXアプリケーションの設計と構造について、どのようなアプローチが最も効果的かを示す本当に良いケーススタディはまだ見つけていません。

編集:イベント処理に関する追加コメント

イベント処理-詳細に行き詰まることなく、モデルのメソッドを直接呼び出すUIコンポーネント内のイベントハンドラー、またはコンテキストから現在のドメインオブジェクトを取得してそのメソッドを呼び出すActionクラスがあります。

例1-単純なイベントハンドラーを使用します。アイテムのリストを表示するUIコンポーネントを取得します。 UIコンポーネントには、リストビューコントロールと、アイテムを移動するためのいくつかの上下ボタンがあります。 Button.setOnAction()で、UIコンポーネント内のハンドラーメソッドを参照します。そのメソッドはリストを見て、現在選択されている行を取得し、モデルのメソッドを呼び出します。何かのようなもの

dataModel.moveRowUp(currentRow)

モデルは何を変更するかを決定し、アイテムのリストを更新します。リストビューはリストにバインドされているため、表示される項目は自動的に変更されます。

明らかに、これはUIコンポーネントをモデルにかなり密接に結び付けています。ただし、私のコードでは、適切に表示するためにUIコードがモデルを理解する必要があるため、これは許容できるトレードオフです。

例2-アクションの使用これは、複数の場所からイベントを発生させたい場合に使用します。たとえば、注文明細を削除するコマンドがあります。これは、メニューコマンド、ツールバーボタン、ホットキー、または注文テーブルのポップアップ右クリックメニューから呼び出すことができます。すべての場合において、効果は同じである必要があります-ドメインオブジェクトのメソッドを呼び出して行を削除します。

この場合、ActionDeleteOrderLineなどのActionオブジェクトがあります。メニューには、ツールバーボタンと同様に、インスタンスがバインドされています。メニューコマンドが呼び出されると、アクションのイベントハンドラーメソッドが呼び出されます。このイベントハンドラーは、現在の選択コンテ​​キスト(つまり、ユーザーが現在操作している/選択したドメインオブジェクト)を取得し、ドメインオブジェクトの適切なメソッドを呼び出します。

2
David