이펙티브 자바 - item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

이펙티브 자바 - item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

클래스들이 자원(다른 클래스)에 의존하는 경우가 있다. 예를들어 SpellChecker 클래스가 dictionary 유틸리티 클래스를 사용한다고 가정한다.

유틸리티 클래스

public class SpellChecker {
	private static final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 인스턴스화 방지 (아이템 4 참고)
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

싱글턴

public class SpellChecker {
	private final Lexicon dictionary = ...;
    
    private SpellChecker() {} // 인스턴스화 방지 (아이템 4 참고)
    public static SpellChecker INSTANCE = new SpellChecker(...);
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

두 방법 모두 확장에 유연하지 않고 테스트가 어렵다.

기본적으로 멀티스레드 환경에서는 사용할 수 없으며, 사전은 굉장히 여러 종류가 있는데(한국어 사전, 영어 사전, 특수 어휘용 사전 등…) dictionary 하나로만 이 역할을 모두 수행하기에는 어렵고 SpellChecker는 dictionary 하나만 사용할 수 있기 때문이다.

사용하는 자원에 따라 동작이 달라지는 클래스는 위 두 방법이 적합하지 않다.

의존 객체 주입

public class SpellChecker {
    private final Lexicon dictionary;
    
    // 여기서 의존성 주입을!
    public SpellChecker(Lexicon dictionary){
    	this.dictionary = Objects.requireNotNull(dictionary);
    }
    
    public static boolean isVaild(String word) {...}
    public static List<String> suggestions(String typo) {...}
}

장점

  • 불변 보장
  • 여러 클라이언트가 의존 객체들을 안심하고 공유 할 수 있다(멀티 스레드 환경)
  • 의존 객체 주입은 생성자 뿐만 아니라, 정적 팩터리, 빌더 모두 똑같이 응용이 가능하다

의존 객체 주입 패턴 활용 - 참조할 자원의 팩토리를 주입하기

자바8에서 제공되는 Supplier를 이용해 팩토리를 주입함으로써 더욱 유연한 객체생성을 지원할 수 있다. 모자이크 객체를 생성할 때 타일 객체가 필요하다고 생각해보자. 타일 객체 자체를 주입할 수 도 있지만, 타일 팩토리를 주입할 수 있다.

단점

의존 객체 주입은 유연성과 테스트 용이성을 개선해주나, 의존성이 수 천개나 되는 큰 프로젝트에서는 오히려 코드를 어지럽게 만들기도 한다.

보통 스프링 프레임워크같은 의존 객체 주입 프레임워크를 사용해 이러한 단점을 해결한다.