작성했던 아래 글을 요약한 글이다.
https://velog.io/@gusals/IoC-DI-by-마틴-파울러
class MovieLister...
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
이 예제에서는 특정 감독이 제작한 영화 목록을 제공하는 컴포넌트를 작성한다.
여기서 살펴보고자 하는 핵심 내용은 finder 객체와 MovieLister 객체를 어떻게 연결할까 하는 점이다.
MovieLister 객체는 영화들이 어떻게 저장되어있는지 알 필요없이 독립적인 메소드가 되길 원한다. 즉, finder 객체와의 의존성을 없애고 싶은 것이다.
문제를 해결하기 위해 finder 객체를 MovieFinder 인터페이스로 정의했다.
public interface MovieFinder {
List findAll();
}
이렇게 함으로써 MovieLister와 MovieFinder의 결합도가 낮아졌지만, 실제 Movie 목록을 구하기 위해 MovieFinder의 구현 클래스를 알아야 한다.
class MovieLister...
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt"); // 콤마로 구분된 영화 정보를 담고 있는 파일로부터 영화 목록을 읽어온다.
}
코드를 혼자 사용하기에는 괜찮지만, 다른 사람이 클래스를 사용하고 싶어한다면 어떻게 될까?

또한 위 그림과 같이 MovieLister가 MovieFinder 인터페이스와 그 구현 클래스에 모두 의존하게 된다.
원하는 것은 인터페이스에만 의존하고, 실제 구현 클래스에는 의존하지 않는 것인데 어떻게 해야할까?
Plugin 패턴으로 해결 할 수 있다.

이제 문제는 이 플러그인을 어떻게 애플리케이션에 조립해서 넣냐는 것이다. 이것은 경량 컨테이너들이 직면한 주요 문제 중 하나이며, 보편적으로 IoC를 사용함으로써 해결할 수 있다.
IoC가 무엇일까?
사용자로부터 몇가지 정보를 얻는 커멘드라인 프로그램을 작성한다고 생각해보자.
#ruby
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)
하지만 같을 일을 하기 위해 윈도우 시스템을 사용한다면 다음과 같을 것이다.
require 'tk'
root = TkRoot.new()
name_label = TkLabel.new() {text "What is Your Name?"}
name_label.pack
name = TkEntry.new(root).pack
name.bind("FocusOut") {process_name(name)}
quest_label = TkLabel.new() {text "What is Your Quest?"}
quest_label.pack
quest = TkEntry.new(root).pack
quest.bind("FocusOut") {process_quest(quest)}
Tk.mainloop()
위 두 프로그램은 제어의 흐름에 있어서 큰 차이점을 가지고 있다. 특히 process_name과 process_quest를 호출하는 시점에 대한 제어가 다르다.
커멘드라인 프로그램의 경우 메소드들이 호출되는 시점을 직접 제어했지만, 윈도우 프로그램은 직접 제어하지 않고 Tk.mainloop() 를 사용하여 윈도우 시스템에게 제어권을 넘겨주었다.
윈도우 시스템은 내가 폼을 생성할 때 만든 바인딩을 이용해 내 메소드들을 호출할 시점을 결정한다. 즉, 제어가 역전된 것이다. 내가 프레임워크를 호출하는 것이 아니라 프레임워크가 나를 호출하는 것이다. 이 현상을 IoC라고 한다.
경량 컨테이너에서 IoC가 적용되는 부분은 컨테이너가 어떻게 플러그인 구현체를 검색하는지에 대한 것이다.
앞의 예제에서 MovieLister는 finder 구현체를 찾기 위해 구현 클래스의 인스턴스를 생성했다. 이것은 finder를 플러그인될 수 없게 만든다. 컨테이너들은 별도의 조립 모듈에서 MovieLister에 finder 구현체를 연결할 수 있도록 함으로써 어떤 사용자든지 지정된 방식으로 플러그인 할 수 있도록 해야 한다.
IoC는 너무 범용적인 용어이기 때문에 사람들이 혼동할 가능성이 있어 여러 사람들과 논의한 끝에 DI(Dependency Injection)이라는 이름을 만들어냈다.
DI의 기본 아이디어는 객체들을 연결해주는 별도의 객체를 갖는 것이다. 이 조립 객체는 MovieLister 클래스에 알맞은 MovieFinder 구현체를 할당해준다.

DI에는 생성자 주입, Setter 주입, 인터페이스 주입이 있다.
어떤 방식을 사용하는게 좋을까?