この記事は JavaFX Advent Calendar 2016 の6日目です。昨日は @makoke さんの Gluon Mobile(無料)を試してみる でした。あすは @Ziphil さんです。
概要
JavaOne 2016 で聞いてきた CON3927: Flux Architecture with JavaFX の内容をお送りします。
CON3927: Flux Architecture with JavaFX
Speaker | Manuel Mauky @manuel_mauky |
---|---|
Slide | https://static.rainfocus.com/oracle/oow16/sess/1462804682452001cMeJ/ppt/Flux%20with%20JavaFX%20(JavaOne%202016).pdf |
Abstract
With React.JS and the flux architecture, Facebook brought new ideas into the world of front-end development. The core idea of flux is to restrict unidirectional data flow between all components and to explicitly model user interactions. The goal is a better understanding of what is happening inside the application. But JavaScript developers aren’t the only ones who can profit from this architecture pattern. With some modifications, even JavaFX developers can use flux to structure their applications. This session gives an introduction to the flux architecture in general and shows how developers can implement flux with JavaFX by using some reactive programming techniques such as the data binding capabilities of JavaFX and reactive streams.
感想
まず Flux アーキテクチャというのがどんなものかを解説してから、それを JavaFX でどうやって実装していくのかを解説していました。Flux アーキテクチャ自体は Web アプリケーションのために考え出されたものですが、ユーザ入力を受け付けるフロントエンドを持つアプリケーションという点では JavaFX (やスマートフォンアプリ)でも共通していて、同じデザインパターンを適用できる可能性があるという気づきがありました。今後は その辺もより見ていく必要があると感じました。
まず、 Flux アーキテクチャとは何か?
- Facebook 発、Front-End 用のアーキテクチャパターン
- データの流れを一方通行にすることで、アプリケーションの構造をシンプルにできる
- 特定の Framework に依存するものではない
Flux アーキテクチャの利点
- バグがどこにあるのか探すのが容易
- フロントエンドのコードのテストが容易
- Undo/Redo の実装が容易
- イベントソースの実装が容易
- より関数指向な開発スタイルが可能
日本語の解説は「Fluxデザインパターンの勉強」をご参照ください。
Core idea: unidirectional data flow
データの流れを一方通行にすることでシンプルにする
Store
- アプリケーションの状態/ロジック/ビジネスユニットの表現 を持つ
- Setter は持たない(=外側から直接値を操作させない)
- 「受け取ったデータから、どう振る舞うか」はここで実装する
View
- Store オブジェクトと Controll を持つ
- Store のデータを表示
- Store のデータが更新されたら表示内容を更新
Action
- View で受け付けたユーザ入力に応じて生成される
- Type (Actionの種類) と Payload (Actionの値)を持つ
- GoF の Command Pattern に類似
- 実際のコードを見ると、「どう振る舞わせるか?」を実装するのではなく、「何の値でどのActionを実行させるか」を保持させる模様
{
"Type": "CREATE_USER",
"Payload": {
"name": "John",
"age": 12
}
}
Dispatcher
- Reactive Streams を持つ
- すべての Action をすべての Stores に渡す役目をする
- 同期的に動作する
Flux アーキテクチャを JavaFX でどのように実装するのか?
JavaFX に不足しているもの
- JavaFX の Scenegraph には Virtual-DOM に対応する仕組みも API もない
- not fully declarative …… ListView を定義するにも FXML だけでは完結せず、冗長なクラス定義が必要
しかしながら、 JavaFX は Reactive programming 対応できる
実装に向けてのアイデア
- Store は内部プロパティを利用
- Stores は外部に read-only properties を提供
- View は Store の read-only properties をバインド
- Action は Command Pattern でクラスを実装
- Dispatcher は Reactive Stream
- Stores は Dispatcher stream に対し subscribe
Example Code
クリックすると数値がカウントアップするカウンターのアプリケーションで考える。
public class CounterStore {
private IntegerProperty counter = new SimpleIntegerProperty();
public CounterStore() {
Dispatcher.getInstance()
.getActionStream()
.filter(a -> a.getClass().equals(IncreaseAction.class))
.map(a -> (IncreaseAction) a) // cast is needed
.subscribe(this::increase);
}
private void increase(IncreaseAction action) {
counter.set(counter.get() + action.getAmount());
}
public ReadOnlyIntegerProperty counterValue() {
return counter;
}
}
public class CounterView {
@FXML
private Lable valueLabel;
@Inject
private CounterStore store;
public void initialize() {
valueLabel.textProperty().bind(store.counterValue());
}
@FXML
public void increaseButtonPressed () {
Dispatcher.getInstance().dispatch(new IncreaseAction(1));
}
}
public class IncreaseAction {
private final int amount;
public IncreaseAction(int amount) {
this.amount = amount;
}
public int getAmount() {
return amount;
}
// equals & hashcode
}
import org.reactfx.EventSource;
import org.reactfx.EventStream;
public class Dispatcher {
private EventSource<Action> actionStream = new EventSource<>();
public void dispatch(Action action) {
actionStream.push(action);
}
public EventStream<Action> getActionStream() {
return actionStream;
}
}
Demo
All check のチェックボックス実装で役立つ……1つでもチェックが外れたら All のチェックを外す
マウスオーバー時だけ出る delete button はどう実装するか?
JavaFX では1行で書けてしまう。が、Functional Programming じゃない。
deleteButton.visibleProperty().bind(root.hoverProperty);
アイデア
類似するアイデア
- CQRS(Command Query Responsibility Segmentation) + Event Sourcing
- Redux
Virtual SceneGraph のようなもの?
JavaFX 標準には Virtual-DOM に当たる Virtual SceneGraph がないとのことで今回のような実装となったが、 templateFXというものがあるとのこと
関数指向の言語を使う?
- Groovy
- Kotlin
- Frege
Links
- TodoMvcFX
- Example Code……今回実装した Flux Architecture の Framework(お試し実装)、 Reactive Streams には ReactFX を活用している
MVVM Pattern
JavaOne 2016 では JavaFX で MVVM(Model-View-ViewModel) パターンを実装する方法についてもセッションがありました(CON2592: Creating JavaFX Applications with mvvmFX)。こちらはすでに mvvmFX としてフレームワークが公開されています。新規の JavaFX プロジェクトで採用したいフレームワークを探している場合は、こちらを調査してみてもよいかもしれません。