質問: new UserRepository ();` として実装クラスのインスタンスを作ってから、 `userRepository.getUser(userId);` を実行すると思うのですが、 **上記の例(実行クラスで`IUserRepository userRepository`を宣言して使う)というのは、 どういった仕組みでインターフェースを使っているのでしょうか?** **また、 もし、2つ目の実装クラス`UserRepository2`みたいのが出来た場合、 `UserRepository` と `UserRepository2` のどちらの実装が使われるかを 実行クラスでどうやって判別すればよいでしょうか?** このようなインターフェースの使い方は他でも見たことあるので、文法自体は間違っていないと思われるのですが、理解がむずかしくハマっています。 --- **追記** 例えば、以下のspring bootサンプルコードでもControllerからインターフェースを呼んでいますが、どの実装クラスを使っているかは指定していないです。この仕組みが良くわかりません。
回答: > > 上記の例(実行クラスでIUserRepository userRepositoryを宣言して使う)というのは、 > > どういった仕組みでインターフェースを使っているのでしょうか? > > > そのようなJavaのサンプルコードでは、setterやコンストラクタが省略されていることが多いので、適宜補完して読み替える必要があります。 ``` class SomeApplicationService { IUserRepository userRepository; // 実際にはコンストラクタや、 public SomeApplicationService(IUserRepository userRepository) { this.userRepository = userRepository; } // setter が備わっていると考える public void setUserRepository(IUserRepository userRepository) { this.userRepository = userRepository; } ... } ``` これらのメソッドは、適切なタイミングで誰かによって呼び出されている想定で読み進めることになります。 Spring BootなどのDI framework上で動作させるのであれば、「誰か」というのはframeworkということになるでしょう。 > > また、もし2つ目の実装クラスUserRepository2みたいのが出来た場合、 > > UserRepositoryとUserRepository2のどちらの実装が使われるかを > > 実行クラスでどうやって判別すればよいでしょうか? > > > Springのタグが付いているのでそちらの実装で説明すると、利用可能な実装クラス(component)は起動時に一覧化されます(component scanning)。 このとき複数のcomponentが見つかればエラーになります(起動しません)。 この場合、コード上で明示的にcomponentに印を付けておき、どちらのcomponentを利用するか明示する必要があります。 具体的には、 * どれかひとつに`@Primary`が付いていればframeworkはそれを利用する * `@Qualifier`で、利用したいcomponentのqualifierを指定する といった実装を行うことになります。 > > 例えば、以下のspring bootサンプルコードでもControllerからインターフェースを呼んでいますが、どの実装クラスを使っているかは指定していないです。この仕組みが良くわかりません。 > > > 当てはまるクラスが1つだけの場合はそれが利用されます。 (前述の通り、複数ある場合はエラーになります) (コメントより) > > SpringのDIでinterfaceの実装クラスが2つある場合にエラーになるのだとすると、Interfaceの説明で良く言われる「実装クラスが増えたとしても、呼び出し元はそれを意識しないで済むので便利」というメリットを享受できない > > > 大抵の場合、プロダクションコード上でインジェクション対象は唯一ですが、にもかかわらず別の実装で挿げ替えられるようにするのは次のような状況を想定しているためです: * ユニットテストでインジェクション対象をモック化する + フレームワークを使わない(「誰か」は自分なのでframeworkのルールに従わず挿入できる) + component scanning対象の操作(例えばクラスパスの変更によって) * プロダクション環境とは異なる環境(例えばローカルの開発環境)で実行するための実装切り替え + component scanning対象の操作(例えば`ACTIVE_PROFILES_SPIRNG`環境変数によって) 簡単に言うと、実装は確かに複数用意しますが、それらの内どれが実際にインジェクションされるべきかは**起動時に**指定するので問題になりません。 (以下、余談気味になりますが) 他方、プロダクションコード上に複数実装を用意しておき、起動時でなく**実行中に**実装を挿げ替えたい、というような場合も無くは無いでしょう。 そのような場合には、ここまでに登場したようにDIをそのまま使うのではなく、 Service Locator Pattern (適切な説明が見つからなかったのでGoogle検索結果リンク)などで実現することになるかと思います。 次のリンクにサンプル実装があります: * Annotation based ServiceLocatorFactoryBean? - Stack Overflow この例では、 `@Bean(name = "jsonParser")`, `@Bean(name = "xmlParser")` という指定で名前を付けておき、利用時に名前を指定することで、どちらを利用するかを明示しています。 この場合は、そもそもインジェクションの仕組みを直接利用しているわけではない(`Parser`を`@Autowired`しているわけではない)ので問題になりません。