객체 생성과 파괴(Item 5)
Item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
자원을 직접 명시한다는 것은 해당 클래스에서 사용할 객체를 직접 생성해 사용한다는 뜻이다. 다음 코드를 보자.
public class SpellChecker {
private final Dictionary dictionary = new DefaultDictionary(); // 자원을 직접 명시한 경우
private SpellChecker() {}
public static final SpellChecker INSTANCE = new SpellChecker();
public boolean isValid(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
위 코드에서 SpellChecker
객체는 DefaultDictionary
객체를 내부에서 직접 생성하여 사용하고 있다.
자원을 직접 명시하면 좋지 않은 이유
허나 이것은 좋지 않은 코드라고 할 수 있겠다.
그 이유를 살펴보기에 앞서 SpellChecker
클래스의 용도를 알아야 하는데,
SpellChecker
객체는 단어를 확인하는 역할을 담당하고 있다.
단어를 확인하기 위해 사전 즉, Dictionary
객체를 이용해야 한다.
위 코드에서 SpellChecker
클래스는 내부 필드로 Dictionary
객체를 직접 생성하여 다룬다.
그렇다면 사전은 단 하나인가? 사전은 언어별로 다를 수도 있고 심지어 테스트를 위해 가짜 사전 객체를 넣어주어야 할 때도 있을 것이다.
위의 SpellChecker
객체에는 다른 사전 객체를 넣어줄 수가 없다(dictionary
필드를 바꿀 수 있는 메서드를 제공할 수도 있으나 매우 비효율적이며 특히 멀티 쓰레드 환경에서 많은 버그를 발생시킨다).
이런 설계는 OCP 원칙에도 위배된다. 사전이 추가되더라도 SpellChecker
클래스는 재사용할 수 없고 SpellChecker
클래스와 동일한 용도의 또 다른 클래스를 정의해야 할 것이다.
이렇게 어떤 자원(객체)에 따라 유동적인 동작을 해야하는 클래스는 싱글톤이나 유틸리티 방식이 적합하지 않다. 이런 클래스는 의존 객체 주입을 사용하는 것이 아주 효율적이다.
의존 객체 주입을 사용할 때 장점
의존 객체 주입이란?
객체를 내부에서 직접 명시하는 것이 아니라 외부에서 주입해주는 방식을 말한다.
public class SpellChecker {
private final Dictionary dictionary;
public SpellChecker(Dictionary dictionary) {
this.dictionary = dictionary;
}
public boolean isValid(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
위 코드에서 SpellChecker
클래스는 Dictionary
객체를 주입 받아서 생성된다.
물론 Dictionary
객체는 추상 클래스 혹은 인터페이스 타입이라야 적절할 것이다(그래야 다형성을 활용한 여러 하위 타입의 객체를 받을 수 있기 때문).
이 클래스는 어떤 사전을 가져오더라도 활용될 수 있다. 즉, 재사용 가능한 클래스가 된다.
영어 사전, 한글 사전 등 언어별로 다른 사전 객체 뿐만 아니라 테스트를 위한 가짜 사전 객체도 주입해줄 수 있다.
이 코드는 아주 유연하며 사전 객체를 매우 쉽게 확장할 수 있게 도와주고 테스트도 용이하다.
[참고]
백기선, inflearn 강의 이펙티브 자바 완벽 공략 1부
Joshua Bloch, 이펙티브 자바