導入
第4章でTaskServiceにTaskRepositoryをコンストラクタで渡しました(コンストラクタインジェクション)。ただしアプリの依存が増えてくると、「誰が何を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);
- コンテナは各クラスのコンストラクタを見て、必要な依存を再帰的に解決する。
TaskListViewModelがTaskServiceを、TaskServiceがTaskRepositoryを要求していることまで自動で辿ってくれる - 多くのDIコンテナは寿命(ライフサイクル)も管理する。「アプリ全体で1つのインスタンスを使い回す」(Singleton)か、「要求されるたびに新しいインスタンスを作る」(Prototype)かを設定で切り替えられる
- 手で
newを書く場合との違いは「自動化」だけで、DIそのものの考え方(抽象への依存・コンストラクタインジェクション)は変わらない
このコースのブラウザ環境にはSpringやGuiceが無いため、次のレッスンでは同じ考え方を手動で体験します。
まとめ
- DIコンテナは「登録(この抽象にはこの実装)→ 解決(コンストラクタを辿って自動で組み立てる)」を自動化する道具
- Spring・Guiceが代表例。実務のJavaアプリの多くがどちらかを採用している
- コンテナが無くても、DIという設計そのもの(抽象への依存・コンストラクタインジェクション)は変わらない
次回: 実際に依存を手で組み立てる場所、Composition Rootです。