[번역] 제어 역전 컨테이너와 의존성 주입 패턴 (3/4)

[번역] 제어 역전 컨테이너와 의존성 주입 패턴 [1][2][3][4]


서비스 위치탐색기(Service Locator) 사용

MovieLister 클래스가 MovieFinder의 구현물에 종속되지 않게 하는 것이 의존성 주입으로 얻을 수 있는 주요 이점 입니다. 이로써 영화 목록 프로그램을 친구들에게 줘서 각자가 자기 환경에 적합한 구현물을 선택하도록 할 수 있게 되었습니다. 그러나 주입이 이 의존성을 끊는 유일한 방법은 아닙니다. 또 다른 방법은 서비스 위치탐색기를 쓰는 것입니다.

서비스 위치탐색기 뒤에 있는 기본적인 생각은 응용 프로그램이 필요로 할 서비스들의 전체 소유권을 어떻게 취할지 알고 있는 어떤 객체를 두자는 것입니다. 따라서 이 응용 프로그램의 서비스 위치탐색기에는 필요시 영화 찾기 객체를 반환하는 메소드가 하나 있을 것입니다. 물론 이는 부담을 살짝 미룬 것 뿐입니다. 여전히 목록 객체에게 위치탐색기를 줘야 합니다.`\ 결과적인 의존성은 그림 3과 같습니다.

그림 3 : 서비스 위치탐색기에서 의존성

이 경우에는 ServiceLocator을 싱글톤 등기소(Registry)로 쓰려합니다. 그리고 목록 객체는 인스턴스화 할 때 이 저장소를 써서 찾기 객체를 얻습니다.
class MovieLister...
MovieFinder finder = ServiceLocator.movieFinder();
class ServiceLocator...
public static MovieFinder movieFinder() {
return soleInstance.movieFinder;
}
private static ServiceLocator soleInstance;
private MovieFinder movieFinder;
주입 방식에서처럼 서비스 위치탐색기를 설정해야만 합니다. 여기서는 코드로 설정했지만 설정 파일에서 적절한 데이터를 읽는 방식을 쓰는 게 어렵지는 않습니다.
class Tester...
private void configure() {
ServiceLocator.load(new ServiceLocator(new ColonMovieFinder("movies1.txt")));
}
class ServiceLocator...
public static void load(ServiceLocator arg) {
soleInstance = arg;
}

public ServiceLocator(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
테스트 코드는 이렇습니다.
class Tester...
public void testSimple() {
configure();
MovieLister lister = new MovieLister();
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
종종 서비스 위치탐색기 같은 방식은 구현물을 교체할 수 없기 때문에 테스트하기 힘들어 좋지 않다는 듣습니다. 분명히 이런 종류의 문제에 빠지만한 나쁜 설계를 할 수 있습니다. 그렇지만 반드시 그렇게 되는 것은 아닙니다. 이 경우 서비스 위치탐색기 예는 단순히 데이터를 담고 있있기만 합니다. 테스트용으로 만든 서비스로 위치탐색기를 쉽게 만들 수 있습니다.

보다 정교한 위치탐색기를 쓰려면 서비스 위치탐색기의 서브클래스를 만들고 그 서브클래스를 등기소의 클래스 변수에 전달하도록 할 수 있습니다. 직접 인스턴스 변수에 접근하지 않고 인스턴스의 메소드를 호출하도록 정적 메소드(static method)를 바꾸도 좋습니다. 쓰레드에서만 쓰는 저장소를 써서 쓰레드 전용 위치탐색기를 제공할 수도 있습니다. 이 모든 것을 서비스 위치탐색기를 사용하는 쪽 코드를 바꾸지 않고도 할 수 있습니다.

서비스 위치탐색기는 싱글톤이 아니라 등기소로 봐야합니다. 싱글톤은 등기소를 구현하는 손 쉬운 방법이기는 하지만 이 구현 방식은 쉽게 바뀔 수 있습니다.

위치탐색기용 격리 인터페이스 사용하기

지금까지의 단순한 접근법은 MovieLister가 전체 서비스 위치탐색기 클래스에 의존한다는 문제가 있습니다. 심지어 필요한 서비스가 하나 뿐인데도 말입니다. 격리 인터페이스(segregated interface)를 써서 이 문제를 완화 할 수 있습니다. 전체 서비스 위치탐색기 인터페이스를 쓰는 대신 목록 객체는 필요한 일부 인터페이스만 선언하도록 할 수 있습니다.

이 상황에서 lister 인터페이스를 제공하는 측에서는 finder의 소유권을 얻는 데 필요한 위치탐색기 인터페이스도 제공해야 할 것입니다.
public interface MovieFinderLocator {
public MovieFinder movieFinder();
이제 찾기 객체에 접근하도록 하려면 위치탐색기가 이 인터페이스를 구현해야 합니다.
    MovieFinderLocator locator = ServiceLocator.locator();
MovieFinder finder = locator.movieFinder();
   public static ServiceLocator locator() {
return soleInstance;
}
public MovieFinder movieFinder() {
return movieFinder;
}
private static ServiceLocator soleInstance;
private MovieFinder movieFinder;
눈치챘겠지만 인터페이스를 사용하기로 한 이상 더는 정적 메소드로 서비스에 접근할 수 없습니다. 위치탐색기의 인스턴스를 얻고 이것을 써서 필요로 하는 것을 얻으려면 클래스를 사용해야 합니다.

동적 서비스 위치탐색기

위의 예제는 정적이었습니다. 서비스 위치탐색기는 외부에서 필요로하는 서비스 마다 메소드를 하나씩 가지고 있습니다. 이것만이 유일한 방법은 아닙니다. 동적 서비스 위치탐색기를 만들어 원하는 어떤 서비스도 보관할 수 있고 실행시에 선택하도록 할 수 있습니다.

이번에는 서비스 위치탐색기가 서비스를 하나씩 저장하는 필드를 가지고 있지 않고 맵(map)을 사용하며 서비스를 얻고 등록하는 범용 메소드를 가지고 있습니다.
class ServiceLocator...
private static ServiceLocator soleInstance;
public static void load(ServiceLocator arg) {
soleInstance = arg;
}
private Map services = new HashMap();
public static Object getService(String key){
return soleInstance.services.get(key);
}
public void loadService (String key, Object service) {
services.put(key, service);
}
설정할 때에는 적당한 키로 서비스를 등록합니다.
class Tester...
private void configure() {
ServiceLocator locator = new ServiceLocator();
locator.loadService("MovieFinder", new ColonMovieFinder("movies1.txt"));
ServiceLocator.load(locator);
}
같은 키 값을 써서 이 서비스를 사용합니다.
class MovieLister...
MovieFinder finder = (MovieFinder) ServiceLocator.getService("MovieFinder");
저는 전적으로 이 방식을 좋아하지 않습니다. 확실히 유연하기는 하지만 그다지 명시적이지 않습니다. 서비스를 찾을 수 있는 유일한 방법은 문자적인 키 뿐입니다. 전 명시적인 방법을 좋아합니다. 인터페이스 선언을 보면 찾고자 하는 것이 어디에 있는지 알 수 있기 때문입니다.

아발론(Avalon)으로 위치탐색기와 주입을 동시에 사용하기

의존성 주입과 서비스 위치탐색기는 꼭 상호 배타적인 개념인 건 아닙니다. 둘을 동시에 사용하는 좋은 예가 아발론 프레임워크입니다. 아발론은 서비스 위치탐색기를 사용합니다. 하지만, 위치탐색기가 어디에서 있는지 알려줄 때는 주입을 사용합니다.

Berin Loritsch가 아발론을 사용한 우리 예제의 단순한 버전을 보내왔습니다.
public class MyMovieLister implements MovieLister, Serviceable {
private MovieFinder finder;

public void service( ServiceManager manager ) throws ServiceException {
finder = (MovieFinder)manager.lookup("finder");
}
이 service 메소드는 컨테이너가 MyMovieLister에게 서비스 메니저를 알려줄 수 있도록 하는 인터페이스 주입의 예입니다. 이 서비스 메니저는 서비스 위치탐색기의 한 예입니다. 이 예에서 목록 객체는 메니저를 필드에 보관하지 않습니다. 대신, 메니저를 써서 바로 찾기 객체를 찾아 저장합니다.

[번역] 제어 역전 컨테이너와 의존성 주입 패턴 [1][2][3][4]


이 글과 관련있는 글을 자동검색한 결과입니다 [?]

by 박성철 | 2009/10/11 13:37 | 프로그래밍 이야기 | 트랙백 | 덧글(0)

트랙백 주소 : http://gyumee.egloos.com/tb/2605951
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

◀ 이전 페이지다음 페이지 ▶