本文へスキップ
BecomeCoder

Java MVVMコース · 第8章 周辺の道具立て ― DI・ヘルパー・アンチパターン · レッスン31

DIコンテナの考え方 ― 手動DIから自動化へ

ローカル実施

導入

第4章TaskServiceTaskRepositoryをコンストラクタで渡しました(コンストラクタインジェクション)。ただしアプリの依存が増えてくると、「誰が何をnewして、どの順で渡すか」を手であちこち書くのは大変になります。SpringやGuiceといったDIコンテナは、この組み立てを自動化する道具です。

説明

flowchart LR
    R["登録<br/>「TaskRepositoryが要求されたらInMemoryTaskRepositoryを使う」"] --> C["コンテナ"]
    C -->|解決| G["context.getBean(TaskListViewModel.class)"]
    G --> V["依存が全部組み立てられたTaskListViewModel"]
    style C fill:#e1f5fe

たとえばSpringを使うと、次のように書けます(読み物:この講座のシミュレータにSpringは無いため、実行はできません)。

// 登録: このクラスが必要とする依存を、コンテナが後で自動的に解決できるよう印を付ける
@Component
public class TaskService {
    private final TaskRepository repository;

    public TaskService(TaskRepository repository) {  // コンストラクタを見て、コンテナが自動的に注入する
        this.repository = repository;
    }
}

@Configuration
public class AppConfig {
    @Bean
    public TaskRepository taskRepository() {
        return new InMemoryTaskRepository();
    }
}

// 解決: TaskListViewModelが要求するTaskServiceも、
//       TaskServiceが要求するTaskRepositoryも、まとめて自動で組み立てられる
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
TaskListViewModel viewModel = context.getBean(TaskListViewModel.class);
  • コンテナは各クラスコンストラクタを見て、必要な依存を再帰的に解決する。TaskListViewModelTaskServiceを、TaskServiceTaskRepositoryを要求していることまで自動で辿ってくれる
  • 多くのDIコンテナは寿命(ライフサイクル)も管理する。「アプリ全体で1つのインスタンスを使い回す」(Singleton)か、「要求されるたびに新しいインスタンスを作る」(Prototype)かを設定で切り替えられる
  • 手でnewを書く場合との違いは「自動化」だけで、DIそのものの考え方(抽象への依存・コンストラクタインジェクション)は変わらない

このコースのブラウザ環境にはSpringやGuiceが無いため、次のレッスンでは同じ考え方を手動で体験します。

まとめ

  • DIコンテナは「登録(この抽象にはこの実装)→ 解決(コンストラクタを辿って自動で組み立てる)」を自動化する道具
  • Spring・Guiceが代表例。実務のJavaアプリの多くがどちらかを採用している
  • コンテナが無くても、DIという設計そのもの(抽象への依存・コンストラクタインジェクション)は変わらない

次回: 実際に依存を手で組み立てる場所、Composition Rootです。