728x90
반응형
많은 클래스가 하나이상의 자원에 의존한다.
정적 유틸리티 클래스를 예로 들어볼 수 있다.
정적유틸리티 잘못 사용한 예 - 유연하지 않고 테스트 하기 어렵다.
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 static final Lexicon dictionary = ...;
private SpellChecker() {}
public static SpellChecker INSTANCE = new SpellChecker(...);
public static boolean isValid(String word) {...}
public static List<String> suggestions(String typo) { ...}
}
위 두 가지 방식 모두 사전을 단 하나만 사용한다고 가정한 점에서 유연하지 않은 방식이다.
SpellChecker가 여러 사전을 사용할 수 있도록 만들려면 필드에서 final을 제거하고 다른 사전으로 교체하는 메서드를 추가해도 된다.
그러나 이 방식은 멀티스레드 환경에서 쓸 수 없다.
사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.
인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 의존객체주입을 이용할 수 있다.
의존 객체 주입 - 유연성과 테스트 용이성을 높여준다.
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) {
throw new UnsupportedOperationException();
}
public List<String> suggestions(String typo) {
throw new UnsupportedOperationException();
}
}
public class Dictionary{
public static void main(String[] args) {
Lexicon dictionary = new KoreanDictionary();
SpellChecker spellChecker = new SpellChecker(dictionary);
SpellChecker.isValid("test");
}
}
}
interface Lexicon {}
class KoreanDictionary implements Lexicon { }
자원이 몇 개든 의존 관계가 어떻든 잘 동작하며 불변을 보장하기 때문에 여러 클라이언트가 안심하고 공유할 수 있다.
이 패턴의 변형으로 생성자에 자원팩터리를 넘겨줄 수 있다.
팩터리란 호출할때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 의미한다.
자바8의 Supplier<T> 인터페이스가 팩터리로 쓰기에 완벽하다.
Mosaic create(Supplier<? extends Tile> tileFactory) {...}
위와 같은 방식으로 자신이 명시한 하위타입이면 무엇이든 생성할 수 있는 팩터리를 넘길 수 있다.
Supplier<T>
인자를 받지 않고 Type T 객체를 리턴하는 함수형 인터페이스
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Supplier<Lexicon> dictionary) {
this.dictionary = Objects.requireNonNull(dictionary.get());
}
public static boolean isValid(String word) {
throw new UnsupportedOperationException();
}
public static List<String> suggestions(String typo) {
throw new UnsupportedOperationException();
}
public static void main(String[] args) {
Lexicon dictionary = new KoreanDictionary();
SpellChecker spellChecker = new SpellChecker(() -> dictionary);
spellChecker.isValid("test");
}
}
클래스가 내부적으로 하나 이상의 자원에 의존하고 리소스에 따라 행동을 달리하는 클래스에는 싱글턴과 정적유틸리티 클래스를 사용하지 않는 것이 좋다. 클래스가 이 자원을 직접 만들게 해서도 안된다.
필요한 자원을 생성자에 넘겨주는 의존객체주임을 이용해서 유연성, 재사용성, 테스트 용이성을 개선할 수 있다.
728x90
반응형
'책리뷰 > 이펙티브자바' 카테고리의 다른 글
[이펙티브자바] 아이템7. 다 쓴 객체 참조를 해제하라 (0) | 2022.02.23 |
---|---|
[이펙티브자바] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2022.02.15 |
[이펙티브자바] 아이템4. 인스턴스화를 막으려면 private을 강제하라 (0) | 2022.02.12 |
[이펙티브자바] 아이템3. private생성자나 열거타입으로 싱글턴 보증 (0) | 2022.02.11 |
[이펙티브자바] 아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.02.11 |