Inversion of Control Containers and the Dependency Injection pattern

Tags:

Inversion of Control Containers and the Dependency Injection pattern

In the Java community there’s been a rush of lightweight containers that help to assemble components from different projects into a cohesive application. Underlying these containers is a common pattern to how they perform the wiring, a concept they refer under the very generic name of “Inversion of Control”. In this article I dig into how this pattern works, under the more specific name of “Dependency Injection”, and contrast it with the Service Locator alternative. The choice between them is less important than the principle of separating configuration from use.

어차피 다 잊어버릴것이 뻔하니까 코드를 남기자면,

Constructor Injection with PicoContainer

영화 목록 보여주기 클래스

class MovieLister…
   public MovieLister(MovieFinder finder) {
       this.finder = finder;      
    }

영화 목록을 실제로 찾는 클래스

class ColonMovieFinder…
   public ColonMovieFinder(String filename) {
       this.filename = filename;
    }

container 부분

   private MutablePicoContainer configureContainer() {
       MutablePicoContainer pico = new DefaultPicoContainer();
       Parameter[] finderParams =  {new ConstantParameter(“movies1.txt”)};
       pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
       pico.registerComponentImplementation(MovieLister.class);
       return pico;
    }

테스팅 하는 부분

   public void testWithPico() {
       MutablePicoContainer pico = configureContainer();
       MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class);
       Movie[] movies = lister.moviesDirectedBy(“Sergio Leone”);
       assertEquals(“Once Upon a Time in the West”, movies[0].getTitle());
    }

약간의 해설.
MovieLister는 MovieFinder라는 인터페이스를 사용합니다. 그리고, ColonMovieFinder는 ‘:’를 delimiter 로 사용하는 텍스트파일에대한 검색을 구현한 클래스입니다.

그러면 MovieLister는 MovieFinder의 concrete implemenetation을 필요로 하겠죠? 가장 Naive 한 접근은 MovieLister에 때려넣는 것이고 (즉, MovieLister의 생성자에서 finder = new ColonMovieFinder()라고 하는것) 이 방법의 단점은 나중에 저장소가 콜론으로 분리된 파일이 아닌 다른 곳(XML이나 DB 등)으로 바뀌면 코드를 바꿔야한다는 것이고, 바뀌는 곳이 어딘지 찾기도 힘들다.. 라는 것이겠죠.

또다른 측면은, MovieListener와 MovieFinder가 2개의 독립적인 팀으로부터 설계되어 ‘컴포넌트’ (컴포넌트라고 할때는, 소스코드 변환없이 Binary 상태의 blob으로 전달되어 갖다가 딱 끼워서 곧바로 써먹겠다..라는 의미임)로 전달되어 사용자들이 쓸 수 있게 해야하는데 MovieLister에서 ConlonMovieFinder에 대한 의존이 바이너리 상에 존재한다면 문제겠죠. 이걸 XMLMovieFinder로 바꾸려면 결국 코드 수정해야한단 말이니까.

그럼 이걸 해결하는게 사용과 생성을 분리하는 Factory 패턴이겠죠. 팩토리하면 곧바로 Service Locator와 연결이되어, 아마 다음과 같은 코드가 나오게 될 것입니다.

Service Locator

class MovieLister…
   MovieFinder finder = ServiceLocator.movieFinder();

class ServiceLocator…
   public static MovieFinder movieFinder() {
       return soleInstance.movieFinder;
    }
   private static ServiceLocator soleInstance;
   private MovieFinder movieFinder;

IoC(Inversion of Control)과 Service Locator의 차이중 핵심은 Service Locator 의 경우, MovieLister에서 ServiceLocator로의 의존이 생긴다는 것이죠. 인기도의 측면에서는 IoC가 더 높은 것 같지만.. 저로서는 귀찮은 관계로 Service Locator의 방식을 선호하겠지만.. 뭐.. 모르죠. 어쨌든간에 진짜 key는 객체간의 dependency를 없앤다.. 라는 것입니다. (디커플링..디커플링..디커플링..의 마법주문)

또 한가지.. Dependency Injection은 IoC에 대한 Fowler가 사용하는 명칭인데요.. 개념적으로는 Dependency Injection (즉 injector가 별도 존재하여 의존관계를 삽입한다)라는 용어가 훨씬 말이 되네요..

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *