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

요약

클래스가 내부에 특정 자원에 의존하고, 그 클래스의 행동이 특정 자원에 따라 영향을 많이 받으면, 싱글톤과 정적 유틸리티 클래스로 구현하면 안된다.
이런 영향력이 있는 자원을 클래스 내부에서 만들면 안된다.
대신 이 자원(혹은 자원을 만드는 팩토리)을 해당 클래스를 인스턴스화 할 때 넘겨주자.

자원을 직접 명시하는 잘못된 예시 - 정적 유틸리티 & 싱글턴

맞춤법 검사기가 사전을 자원으로 가진다고 가정하자.
만약 사전을 직접 명시한다는 건 어떤 얘기일까?

1
2
3
4
5
6
7
8
//정적 유틸리티를 활용한 예제
public class SpellChecker {
private static final Dictionary dictionary = new Dictionary(Language.ENGLISH);

private SpellChecker() {} //객체 생성 방지

public static String changeSuggestions(String typo) {...}
}
1
2
3
4
5
6
7
8
//싱글톤을 활용한 예제
public class SpellChecker {
private final Dictionary dictionary = new Dictionary(Language.ENGLISH);
private static SpellChecker INSTANCE = new SpellChecker();

private SpellChecker() {}
public static String changeSuggestions(String typo) {...}
}

위 두 예시 모두 자원인 사전을 final로 직접 명시하고 있다.

  • 나중에 다른 사전을 사용하고 싶을 때 변경이 힘들어진다.
  • final을 제거하고 사전을 교체하는 메서드를 추가하는 방식은 멀티스레드 환경에 쓸 수 없음.

즉 사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식을 사용하면 안된다!

의존 객체 주입 예시 - 생성할 때 필요한 자원 넘겨주기

1
2
3
4
5
6
7
public class SpellChecker {
private final Dictionary dictionary;
public SpellChecker(Dictionary dictionary) {
this.dictionary = dictionary;
}
//메서드..
}
  • 자원이 많거나 의존 관계가 어떻든 잘 작동
  • 불변성을 지원
  • 생성자, 정적 팩터리, 빌더 모두에 적용 가능

자원 팩터리로 의존 객체 주입 - Supplier<T>

1
2
3
4
5
6
7
public class SpellChecker {
private final Dictionary dictionary;
public SpellChecker(Supplier<? extends Dictionary> dictionaryFactory) {
this.dictionary = dictionaryFactory.get();
}
//메서드...
}

자원 팩토리란 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 말한다.
자바에서는 Supplier<T>가 해당한다.

위처럼 생성자(정적 팩토리 메서드나 빌더)에 팩토리를 넘겨받으면 다음같이 활용 가능하다.

1
2
3
4
5
public class Application {
public static void main(String[] args) {
SpellChecker englishChecker = new SpellChecker(() -> new Dictionary(Language.ENGLISH));
}
}
Share