반응형
이 내용은 이펙티브 자바 Effective Java 3/E
를 읽으면서 정리한 내용을 포함하고 있습니다.
많은 클래스는 하나 이상의 자원에 의존하게 됩니다. 책에 있는 예제인 통해 맞춤법 검사기에 대해서 알아보도록 하겠습니다.
부적절한 구현
아래는 static 유틸 클래스를 통해서 구현하였습니다.
public class SpellChecker {
private static final Lexicon dictionary = new KoreanDictionary();
private SpellChecket() {} // 객체 생성 방지
public boolean isValid(final String word) {
throw new UnsupportedOperationException();
}
public List<String> suggestions(final String tyop) {
throw new UnsupportedOperationException();
}
}
interface Lexicon {}
class KoreanDictionary implements Lexicon {
}
class TestDictionary implements Lexicon {
}
- 현재는 한국어 사전을 사용하고 있는데 사전을 바꾸기가 힘듭니다.
아래는 싱글턴을 구현하였습니다.
public class SpellChecker {
private final Lexicon dictionary = new KoreanDictionary();
private SpellChecker() {
}
private static SpellChecker INSTANCE = new SpellChecker() {
};
public boolean isValid(final String word) {
throw new UnsupportedOperationException();
}
public List<String> suggestions(final String tyop) {
throw new UnsupportedOperationException();
}
}
interface Lexicon {}
class KoreanDictionary implements Lexicon {
}
class TestDictionary implements Lexicon {
}
- 사전을 하나만 사용할 것이라면 위와 같은 구현도 만족스러울 수 있겠지만, 실제로는 각 언어와 맞춤법 검사기는 사용하는 사전이 각기 다릅니다. 또한 테스트 코드에서는 테스트용 사전을 사용하고 싶을 수 있습니다.
- 어떤 클래스가 사용하는 리소스에 따라 행동을 달리 해야 하는 경우에는 static 유틸리지 클래스와 싱글톤을 사용하는 것은 부적절합니다.
- 그런 요구 사항을 만족할 수 있는 간단한 패턴으로 생성자를 사용해서 새 인스턴스를 생성할 때 사용할 리소스를 넘겨주는 방법이 있습니다.
적절한 구현
인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식입니다.
public class SpellChecker() {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requiredNonNull(dictionary);
}
public boolean isValid(final String word) {
throw new UnsupportedOperationException();
}
public List<String> suggestions(final String tyop) {
throw new UnsupportedOperationException();
}
}
interface Lexicon {}
class KoreanDictionary implements Lexicon {
}
class TestDictionary implements Lexicon {
}
- 위와 같은 의존성 주입은 생성자, static 팩토리 그리고 빌더에도 적용할 수 있습니다.
- 이 패턴의 변종으로 리소스의 팩토리를 생성자에 전달하는 방법도 있습니다.
- 이 방법은 자바 8에 들어온
Supplier<T>
인터페이스를 활용하기에 완벽합니다. Supplier<T>
를 인자로 받는 메소드는 보통bounded wildcard type
으로 입력을 제한해야 합니다.
- 이 방법은 자바 8에 들어온
public static SpellChecker create(Supplier<? extends Lexicon> generator) {
return new SpellChecker(generator.get());
}
- 의존성 주입이 유연함과 테스트 용이함을 크게 향상 시켜주지만, 의존성이 많은 큰 프로젝트인 경우에는 코드가 장황해질 수 있습니다. 그 점은 스프링같은 프레임워크를 사용해서 해결할 수 있습니다.
정리
- 클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않는 것이 좋습니다.
- 이 자원들을 클래스가 직접 만들게 해서도 안 됩니다. 대신 필요한 자원을(혹은 그 자원을 만들어주는 팩터리를) 생성자에 (혹은 정적 팩터리나 빌더에) 넘겨주면 됩니다.
- 의존 객체 주입이라 하는 이 기법은 클래스의 유연성, 재사용성, 테스트 용이성을 개선하여 줍니다.
참고
반응형
'독서이야기 > 이펙티브 자바 3 edition' 카테고리의 다른 글
[아이템7] 다 쓴 객체 참조를 해제하라 (0) | 2022.09.03 |
---|---|
[아이템6] 불필요한 객체 생성을 피하라 (0) | 2022.09.03 |
[아이템4] 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.08.07 |
[아이템3] private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2022.08.07 |
[아이템2] 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.08.07 |
댓글