← SEARCHABLE ARCHIVE
Java · 2026-08-12

Java 基礎からマスターまで — 第10回

階層とテストでメンテナンス可能なアプリを作成する

INTERACTIVE READING MODE →

本ブログ作成文は、講義を目指して製作されました。 また、作成された文章の情報も間違っている可能性がありますので、ご注意ください。

プログラムが大きくなるとクラス数より変更が広がる範囲が問題になる。 ルール、リポジトリ、画面境界を分けて動作をテストできるようにすることでJava連載を整理してみよう。

階層の責任

public final class OrderService {
    private final ItemRepository repository;

    public OrderService(ItemRepository repository) {
        this.repository = repository;
    }

    public OrderResult buy(int itemId, int gold) {
        Item item = repository.find(itemId)
            .orElseThrow(() -> new ItemNotFoundException(itemId));

        if (gold < item.getPrice()) {
            return OrderResult.notEnoughGold();
        }

        return OrderResult.success(item, gold - item.getPrice());
    }
}

サービスは購入ルールを担当し、データベースやHTTP詳細実装を直接知らない。 コントローラまたは画面は入力をサービスに渡し、結果を表現します。

依存関係を外に入れる

public interface ItemRepository {
    Optional<Item> find(int id);
}

ItemRepository repository = new FileItemRepository(path);
OrderService service = new OrderService(repository);

マテリアルストアをコンストラクタの外から選択すると、テストではメモリ実装に置き換えることができます。 インターフェイスはすべてのクラスごとに作る装飾ではなく、外部技術が変わる境界に置く。

動作をテストする

@Test
void purchaseKeepsRemainingGold() {
    ItemRepository repository = new MemoryItemRepository(
        new Item(10, "Potion", 30)
    );
    OrderService service = new OrderService(repository);

    OrderResult result = service.buy(10, 100);

    assertEquals(70, result.getRemainingGold());
}

テストは、内部メソッド呼び出しの順序よりも外部から見える規則をチェックします。 実装を整理しても、同じ入力に同じ契約が維持されるかどうかがわかります。

プロジェクトツール

GradleやMavenは、コンパイル、テスト、ライブラリバージョン、パッケージングを繰り返し可能なコマンドにします。 開発環境でのみ実行されるプロジェクトにならないように、ビルドファイルとテストをリポジトリにまとめます。

良いJava構造は階層的な構造ではありません。 状態を変えるルール、外部資源、同時作業の寿命が明確で小さな単位で検証できる仕組みだ。