아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 주는 경우가 있다.
아래의 SpellChecker 클래스는 사전(Dictionary)에 의존하고 있는데 아래처럼 구현하는 경우를 많이 볼 수 있다.
정적 유틸리티 사용
public class SpellChecker {
private static final Lexicon dictionary = new LexiconDictionary();
private SpellChecker() { // 객체 생성 방지
}
private static boolean isValid(String word) {...} // dictionary를 사용한 로직
public static List<String> suggestions(String typo) {
// dictionary를 사용한 로직
}
}
해당 유틸리티 클래스는 사전을 하나만 사용한다고 가정하고 있다. 하지만 현실에서는 사전이 언어마다 따로 있고, 심지어 특수 어휘용 사전을 별도로 두는 경우도 있다.
싱글톤 클래스 사용
public class SpellChecker {
private final Lexicon dictionary = new LexiconDictionary();
public static SpellChecker INSTANCE = new SpellChecker();
private SpellChecker() {
}
public static boolean isValid(String word) {
// dictionary를 사용한 로직
}
public static List<String> suggestions(String typo) {
// dictionary를 사용한 로직
}
}
싱글톤 클래스도 마찬가지로 사전을 하나만 사용한다고 가정하므로 위와 똑같은 단점이 발생한다.
SpellChecker가 여러 종류의 사전을 사용할 수 있도록 하려면 final을 제거하고 dictionary를 변경하는 메소드를 추가할 수 있지만, 오류를 내기 쉬우며 멀티스레드 환경에서는 사용할 수 없다.
대신 인스턴스를 생성할 때 생성자에게 필요한 자원을 넘겨주도록 하면 된다.
해결책) - 의존 객체 주입
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = dictionary;
}
public static boolean isValid(String word) {
// dictionary를 사용한 로직
}
public static List<String> suggestions(String typo) {
// dictionary를 사용한 로직
}
}
정적 클래스와 싱글톤 클래스는 내부적인 자원에 의존하면 안 된다는 사실을 위 예제를 통해 느낄 수 있었다.
즉, 내부 자원은 외부에서 주입을 받는 것이 바람직하다는 뜻이다.
의존 객체를 주입한 방법은 아래와 같은 특징이 있다.
- 자원이 몇 개든 의존 관계가 어떻든 상관없이 작동한다.
- final 키워드 덕분에 불변을 보장하며 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있다.
- 생성자 뿐 아니라, 정적 팩토리와 빌더에서 모두 응용 가능하다.
의존 객체 주입은 단순하게 자원 그 자체를 넘겨주는 방법도 있지만, 자원 팩토리를 넘겨주는 방식도 종종 사용된다.
팩토리란 호출될 때 마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 말한다. 이러한 방식을 팩토리 메서드 패턴이라고 하며, 자바 8에서 Supplier<T>이 팩토리를 표현한 완벽한 예시이다.
public static List<Dog> create(Supplier<? extends Dog> generator) {
...
}
주로 한정적 와일드 카드 타입을 사용하여 팩터리의 타입 매개 변수를 제한한다. 이 방식을 사용하면 클라이언트는 자신이 명시한 타입의 하위 타입이면 무엇이든 팩터리를 넘길 수 있게 된다.
의존 객체 주입은 유연성과 테스트 용이성을 개선해 주지만, 의존성이 매우 많은 프로젝트에서는 비용 소모가 상당히 크다. 이런 경우 의존 객체 프레임워크(Dagger, Guice, Spring 등)을 사용하여 비용을 줄일 수 있다
참고 블로그 문서
'도서 > 정리' 카테고리의 다른 글
[이펙티브 자바] 아이템7. 다 쓴 객체 참조를 해제하라 (0) | 2023.05.17 |
---|---|
[이펙티브 자바] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2023.05.15 |
[이펙티브 자바] 아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (1) | 2023.05.07 |
[이펙티브 자바] 아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2023.05.05 |
[이펙티브 자바] 아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.05.04 |