[EffectiveJava] item05 - 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라.
14 Apr 2022자원을 직접 명시하지 말고 의존 객체 주입을 사용하라.
item04에 static 메소드가 주로 사용되는 util클래스의 경우 생성자를 private로 만들어서 인스턴스화를 막아야 한다고 했지만, 전부 private 생성자를 만들어 막는것은 좋지가 않다.
예를 들어,
Util 클래스가 여러 클래스에 의존하는 경우는 private 생성자를 이용해 막는것이 옳지 않다. 아래 코드를 보자
/* private 생성자로 구현된 경우 */
public class SpellChecker {
private static final Lexicon dictionary = "";
private SpellChecker() {};
public static boolean isValid(String word) {...};
public static List<String> suggestions(String typo) {...};
}
또는 싱글턴으로 구현되는 util 클래스도 있다.
/* 싱글턴으로 구현된 경우 */
public class SpellChecker {
private final Lexicon dictionary = "";
private SpellChecker(...) {}
public static SpellChecker INSTANCE = new SpellChecker(...);
public boolean isValid(String word) {...};
public List<String> suggestions(String typo) {...};
}
사전의 spell체크 util클래스를 만들었다. 하지만 두방식 모두 확장성이 고려되지 않은 하나의 사전만 사용한다고 가정된 점에서 훌륭한 설계가 아니다.
사전의 경우 종류가 다양하다. 영어사전이 있을 수 있고, 국문사전, 더나아가 의학사전 같은 전공관련 사전이 있을 수 있다.
해당 클래스에서 여러 사전을 사용할 수 있도록 확장하기 위해서는 간단한 방법으로 dictionary필드에 final을 제거하고 다른 사전으로 교체하는 메서드를 추가 할 수도 있지만, 해당 방식은 오류를 내기 쉬우며, 멀티스레드 환경에서는 쓸수 없다. (Not Thread-safe)
사용하는 자원에 따라 동작이 달라지는 클래스에서는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.
그렇다면 해당 클래스는 어떻게 변환되는 것이 좋을까? 먼저 요구사항은 SpellChecker클래스가 여러 객체의 인스턴스를 지원 할 수 이써야 하고, 사용자가 원하는 dictionary를 사용할 수 있어야 한다.
해당 방식은 private생성자 및 싱글톤은 제쳐두고 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하면 된다. 즉, 해당 SpellChecker클래스를 생성 할때 의존객체인 사전을 주입해주면 된다.
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) {...};
public List<String> suggestions(String typo) {...};
}
여기서
Objects.requireNonNull
란 특정 객체 참조가 Null인지 확인하고, Null일 경우 사용자 지정 NullPointException을 발생시킨다.이 메서드는 주로 메서드나, 생성자의 매개변수 유효성 검증을 위해 설계되었다. 비슷한 객체로
Optional
이 있지만 사용법이 다르다.
해당 예시 코드는 dictionary라는 하나의 자원만 사용하지만, 자원이 몇개든 의존관계가 어떻든 상관없이 잘 작동한다. 즉 하나의 자원으로 한가지 사전에서 여러 종류의 사전에 대응 할 수 있게 되었다는 뜻이다.
final을 사용하여 dictionary를 불변 객체로 사용하여, 해당 의존객체를 안전하게 공유 할 수 있기도 하다. 해당 의존 객체 주입은 생성자, 정적팩터리, 빌더에도 똑같이 응용 할 수있다.