本文へスキップ
BecomeCoder

Java MVVMコース · 第1章 MVVMとこのコースの進め方 · レッスン2

MVVMとは何か ― View・ViewModel・Modelの役割

ローカル実施

導入

JavaFXアプリを素朴に書くと、ボタンのクリックハンドラの中に業務ロジックやデータ操作を直接書いてしまいがちです。これでは画面を起動しないとロジックを1行もテストできません。MVVM(Model-View-ViewModel)は、この問題を「役割を3つに分ける」ことで解決する設計です。

説明

flowchart LR
    U["ユーザー操作"] --> V["View<br/>(FXML・見た目)"]
    V <-->|バインディング| VM["ViewModel<br/>(画面の状態と操作)"]
    VM -->|呼び出す| M["Model<br/>(Entity・Service など業務ロジック)"]
    M -->|結果を返す| VM

3つの層の責務は、次のように分かれます。

持つもの持たないもの
View見た目(FXML)、レイアウト、色業務ロジック、データアクセス
ViewModel画面の状態(入力中の文字・一覧・ボタンの活性)、画面の操作JavaFXの画面部品そのもの
ModelEntity・値オブジェクト・Service・Repository画面の都合

一番大事なのは ViewModelがJavaFXの画面部品(ButtonTextField)を一切知らない ことです。ViewModelが知っているのは「タイトルという文字列」「タスクの一覧」「追加できるかどうか」といった画面の状態だけ。だからViewModelは、画面を起動しなくても普通のJavaのクラスとしてテストできます。このコースで書くテストのほとんどが、まさにこの性質のおかげで成り立っています。

素朴な書き方との比較

コントローラに直接書くと、こうなります。

// アンチパターン: 画面のイベントハンドラに業務ロジックが直書きされている
@FXML
private void onAddClicked() {
    String title = titleField.getText();
    if (title == null || title.isBlank()) return;
    if (title.length() > 50) return;
    tasks.add(new TaskItem(UUID.randomUUID().toString(), title, false));
    titleField.clear();
    countLabel.setText("残り " + tasks.size() + " 件");
}

「50文字以内」というルールをテストしたいだけなのに、titleFieldcountLabel、つまり画面がないと1行も動かせません。MVVMではこれを分けます。

// ViewModel: 画面部品を知らない。ただのJavaのクラスなのでテストできる
public void add() {
    if (!canAdd()) return;
    taskService.add(newTitle);
    setNewTitle("");
}
public boolean canAdd() {
    return !newTitle.isBlank();
}

MVVMとMVCの違い

Model-View-Controller(MVC)との一番の違いは、ViewとViewModelをつなぐのが「バインディング」であることです。MVCのControllerは「Viewを取ってきて値を入れる」という書き方をしますが、MVVMのViewModelはViewを知りません。ViewModelが自分の状態を変えて「変わったよ」と知らせると、その通知を購読しているViewが勝手に描き直します。この向きの違いが、テストのしやすさを生みます。

まとめ

  • MVVMは View(見た目)/ViewModel(画面の状態と操作)/Model(業務ロジック)の3層
  • ViewModelはJavaFXの画面部品を知らない。だから画面なしでテストできる
  • ViewとViewModelはバインディングでつながる。ViewModelからViewを直接触らない

次回: この3層を実際のパッケージ構成に落とし込みます。