DI(依存性注入)って、結局なにが嬉しいの?
— Springを「雰囲気」で使わないための第一歩 —
はじめに
Springを触っていると、ほぼ確実に出会う言葉があります。
DI(Dependency Injection / 依存性注入)
正直なところ、こんな理解で止まっていませんか。
-
@Autowiredを付けると動く - new しなくていいから楽
- よく分からないけど Spring のお作法
今回はこの「よく分からないけど便利なやつ」を、
Springを一旦忘れて、Javaのコードレベルで整理します。
まず「DIがない世界」を見てみる
いきなりDIの話をすると混乱するので、
まずは DIを使っていないコード から。
public class UserService {
private UserRepository userRepository = new UserRepository();
public void register(String name) {
userRepository.save(name);
}
}
一見、何の問題もなさそうに見えます。
-
UserServiceはUserRepositoryを使う - 普通に new して呼び出している
では、このコードの 何が困る のでしょうか。
問題①:差し替えができない
例えばテストを書きたくなったとします。
@Test
void registerTest() {
UserService service = new UserService();
service.register("test");
}
ここで UserRepository が、
- DBに接続する
- 実際にINSERTする
という実装だったらどうでしょう。
テストのたびにDBが必要になります。
これはなかなかつらい。
問題②:UserServiceが「具体クラス」を知りすぎている
UserService は本来、
「ユーザーを登録する」というビジネスロジック
だけを気にしたいはずです。
しかしこのコードでは、
new UserRepository();
という 生成責任 まで背負っています。
- どのRepositoryを使うか
- どうやって作るか
この判断を UserService がしているのがポイントです。
依存性注入(DI)の考え方
ここでようやくDIの登場です。
DIの本質は、とてもシンプル。
「使う側が、作るな。渡してもらえ」
コードで見るとこうなります。
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void register(String name) {
userRepository.save(name);
}
}
UserService はこう言っています。
- UserRepositoryは必要
- でも「どう作るか」は知らない
- 誰かが用意して渡してくれ
これが 依存性注入 です。
DIによって何が変わったのか
1. 差し替え可能になった
UserRepository mockRepo = new MockUserRepository();
UserService service = new UserService(mockRepo);
- 本番用Repository
- テスト用Repository
を 自由に切り替え られます。
2. 責務が分離された
- UserService
- ビジネスロジックに集中
- 外側のコード
- 依存関係の組み立てを担当
「何をするか」と「どう組み立てるか」が分離されました。
Springは「DIを自動化する仕組み」
ここまで、Springの話は一切していません。
実はこれが重要です。
Springがやっていることは、
-
newの代わりに - DIコンテナが
- 依存関係を組み立てて
- 注入してくれる
ただそれだけ。
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@Service や @Repository は、
DIコンテナへの登録ルール にすぎません。
魔法ではありません。
よくある勘違い
「DI = Spring」
違います。
DIは 設計の考え方、Springは 実装手段 です。
「@Autowired を付ければDI」
付けなくても、コンストラクタインジェクションならDIです。
むしろこちらが推奨されます。
まとめ
DIの嬉しさを一言で言うと、
コードを「柔らかく」すること
- テストしやすい
- 差し替えやすい
- 変更に強い
Springはその土台の上に立っています。
DIが腹落ちすると、
「なぜServiceを分けるのか」
「なぜinterfaceを切るのか」
が自然に見えてきます。
次回予告
次はDIとセットで語られがちな、これ。
「なぜService層を分けるのか?」
Controllerに全部書いてはいけない理由を、
設計目線で掘っていきます。