반응형
사용하는 자원에 따라 동작이 달라지는 클래스는 정적 유틸리티 클래스나 싱글턴 클래스 방식이 적합하지 않다.
- 유연하지 않고 테스트하기 어렵다!!
예를 들어 SpellChecker가 Dictionary를 사용하고 Dictionary를 정적 유틸리티 클래스 혹은 싱글턴 클래스 방식으로 만들었다면 하나의 사전에 대해서만 SpellChecker를 작동시킬 수 있다. 하지만 국적에 따라 다른 사전을 사용한다면 유연하게 변경할 수 없으며 다른 국적의 사전은 테스트도 어려워진다.
// 정적 유틸리티 클래스
public class SpellChecker {
private static final Lexicon dictionary = ...;
private SpellChecker() {} // 객체 생성 방지
public static boolean isValid(String word) { ... }
public static List<String> suggestions(String typo) { ... }
}
의존 객체 주입은 유연성과 테스트 용이성을 높여준다
- 인스턴스를 생성할 때 생성자에 자원을 넘겨주는 방식
- 의존 객체 주입의 형태로 맞춤법 검사기를 생성할 때 의존 객체인 사전을 주입한다
- 의존 객체 주입은 생성자, 정적 팩터리, 빌더 모두에 똑같이 응용할 수 있다.
public class SpellChecker {
private final Lexicon dictionary;
private SpellChecker(Lexicon dictionary) { // 의존성 주입
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}
이 패턴의 쓸만한 변형으로, 생성자에 자원 팩터리를 넘겨주는 방식이 있다.
- 팩터리란 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 말한다 (팩터리 메서드 패턴을 말함)
- Supplier<T> 인터페이스가 팩터리를 표현한 완벽한 예
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get(); // T 타입 객체를 찍어낸다
}
이 방식을 사용해 클라이언트는 자신이 명시한 타입의 하위 타입이라면 무엇이든 생성할 수 있는 팩터리를 넘길 수 있다.
public class SpellChecker {
private final Lexicon dictionary;
private SpellChecker(Supplier<? extends Lexicon> dictionary) {
this.dictionary = dictionary.get(); // 팩터리 메서드 패턴
}
public boolean isValid(String word) { return true; }
public List<String> suggestions(String typo) { return null; }
public static void main(String[] args) {
Lexicon lexicon = new KoreanDictionary();
SpellChecker spellChecker = new SpellChecker(() -> lexicon);
if(spellChecker.isValid("hello")) System.out.println("valid");
}
}
interface Lexicon{}
class KoreanDictionary implements Lexicon {}
// Sys.out "valid"
정리
- 클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않는 것이 좋다
- 이 자원들은 클래스가 직접 만들게 해서도 안된다
- 필요한 자원을 생성자에 넘겨주자(혹은 그 자원을 만들어주는 팩터리를 생성자, 정적 팩터리, 빌더에 넘기자)
- 의존 객체 주입 기법은 클래스의 유연성, 재사용성, 테스트 용이성을 개선해준다
스프링 프레임워크를 사용하면서 의존성 주입은 수도 없이 진행해왔고, 내부적으로 컨테이너에서 수많은 의존성을 주입해주어 개발에 매우 편리함을 제공하였던 경험이 있다. 나중에는 컨테이너에서 의존성을 관리하는 방법도 좀 공부해봐야겠다..
반응형
'Java' 카테고리의 다른 글
Item7 다 쓴 객체 참조를 해제하라 (1) | 2023.01.12 |
---|---|
Item6 불필요한 객체 생성을 피하라 (0) | 2022.12.09 |
Item4 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.12.06 |
Item3 private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2022.12.02 |
Item 2 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.12.01 |