자원을 직접 명시하지 않아야 하는 경우

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴이 적합하지 않다.

자원을 정적으로 명시해두는 것이 부자연스러울 때가 종종 있다. 예를 들어, 맞춤법을 검사하는 프로그램을 아래와 같이 만들었다고 생각해보자. 현실 세계에서는 언어가 바뀔 수도, 특수한 사전을 사용할 수도 있지만 이 코드는 단 하나의 사전만 자원으로 사용한다.

만약 final 한정자를 지우고 사전을 바꿀 수 있는 메소드를 추가한다면 여러 사전을 사용할 수 있겠지만 thread-safe하지 않다.

// 정적 유틸리티 사용
public class SpellChecker {
  private static final Lexicon dictionary = ...;

  private SpellChecker() {}
  ...
}
// 싱글턴 사용
public class SpellChecker {
  private final Lexicon dictionary = ...;

  private SpellChecker() {}
  public static SpellChecker INSTANCE = new SpellChecker(...);
  ...
}

 

자원 명시가 필요한 경우

클라이언트가 명시하는 자원을 클래스가 사용해야 한다면 의존성을 주입해야 한다. 이 경우 클래스의 유연성, 재사용성, 테스트 용이성이 크게 향상된다.

클래스(SpellChecker)가 클라이언트가 원하는 자원(dictionary)을 사용해야 한다면 아래 코드와 같이 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨줘야 한다.

의존 객체 주입 패턴을 사용하면, 불변을 보장하여 같은 자원을 사용하려는 여러 클라이언트가 의존 객체를 공유할 수 있고 생성자, 정적 팩토리, 빌더 모두에 똑같이 응용할 수 있다.

public class SpellChecker {
  private final Lexicon dictionary;

  public SpellChecker(Lexicon dictionary) {
    this.dictionary = Object.requireNonNull(dictionary);
  }
  ...
}

이 패턴을 변형해서 생성자에 특정 타입 인스턴스를 반복해서 넘겨주는 자원 팩터리를 넘길 수 있다. 한정적 와일드카드 타입을 사용해 팩토리 타입 매개변수를 제한하면 클라이언트는 자신이 명시한 타입의 하위 타입 객체를 생성할 수 있는 팩토리를 넘길 수 있다.

 

책 스터디를 하며 한분이 객체 자체를 넘기는 것이 아닌 Supplier를 넘기는 이유에 대해 어떤 분이 질문을 주셨다. 논의해본 결과, 인스턴스를 직접 넘겨준다면 생성자에서 인스턴스를 여러 개 필요로 하는 상황에 대처하기 어려울 수 있다는 결론이 나왔다. 스터디 최고....

Apartment create(Supplier<? extends house> houseFactory) { ... }

 

+ Recent posts