Yomiage
メニュー

Java実践講座 15:不変データと責務の分離で設計する

更新 2026/9/27 6:22:11 公開

投稿者: しゅりりん

到達点は、変更が広がりにくいデータ構造と境界を選ぶことです。前提は第05・06・14回です。

変更できない値は推論しやすい

他の場所から勝手に書き換えられる値は、読む時点ごとに状態を考える必要があります。小さな値を不変にすると、比較、共有、テストが単純になります。ただしrecordを使うだけでは、内部にある可変のオブジェクトまで守れません。

import java.util.*;

public class Main {
    record StudyPlan(String name, List<String> lessons) {
        StudyPlan {
            Objects.requireNonNull(name);
            if (name.isBlank()) throw new IllegalArgumentException("empty name");
            lessons = List.copyOf(lessons);
        }
    }

    public static void main(String[] args) {
        var source = new ArrayList<>(List.of("型", "テスト"));
        var plan = new StudyPlan("Java", source);
        source.add("並行処理");
        System.out.println(plan.lessons());
        System.out.println(plan.equals(new StudyPlan("Java", List.of("型", "テスト"))));
    }
}

Main.javaの出力は型・テストの二項目とtrueです。コンストラクタでリストを防御的にコピーしたので、呼び出し元の追加は計画へ影響しません。今回は要素が不変のStringなので共有しても困りません。可変の要素なら、要素の扱いも検討します。

入出力と判断を分ける

利用者の入力、ファイル、HTTPは外側の境界です。「どのタスクを完了にするか」「割引を認めるか」というルールを、その境界から分けます。ルールが文字列を受け取る必要はなく、検証したTaskや金額型を受け取ればよいのです。

一つの巨大メソッドから、入力変換、業務判断、保存、表示を切り出すと、それぞれを別々に確認できます。ただし小さいプログラムへ、理由なく何十もの抽象クラスを導入すると理解が難しくなります。変更理由が違うものから分け、テストしにくい箇所を境界の発見に使います。

練習と解答

練習:StudyPlanに学習項目を一つ追加する操作を、不変性を維持して設計してください。

解答例:既存のlessonsを新しいArrayListへコピーして項目を加え、そのリストで新しいStudyPlanを返します。元のplanの要素数は変えません。新しい値を返す設計では、呼び出し側が戻り値を使い忘れないことにも注意します。

卒業チェック:「どのデータがどこで変わるか」を説明し、外部通信なしで業務ルールをテストできれば、この回の狙いを達成しています。

公式資料

recordとList.copyOfを参照できます。


Java全20回の目次 | 前の回 | 次の回

このブラウザでは Web Speech API が利用できません。

1 / 16 · 停止中