아이템 5 - 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 - 핵심 정리
이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.
모든 경우에 의존 객체 주입을 사용하는 것이 아니라
사용하는 자원에 따라 동작이 달라지는 클래스의 경우에는 의존 객체 주입을 사용해야한다.
public class Dictionary {
public boolean contains(String word) {
return true;
}
public List<String> closeWordsTo(String typo) {
return Arrays.asList(typo);
}
}
Dictionary 클래스가 있고
public class SpellChecker {
private static final Dictionary dictionary = new Dictionary();
private SpellChecker() {}
public static boolean isValid(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public static List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
Dictionary 클래스를 사용하는 SpellChecker 클래스가 있다.
기능은 어떠한 단어에 대해 Dictionary 클래스를 통해 올바른 단어인지, 비슷한 단어가 있는지
꺼내주는 기능이라고 가정한다.
Dictionary 클래스는 영어에 관련된 사전인지, 독일어에 관련된 사전인이 달라질 수 있다.
사전이 달라질때마다 기능은 똑같지만 새로운 SpellChecker 클래스가 만들어질 것이다.
또한, 테스트시에도 어려움이 많다.
Dictionary 클래스를 바꿔 넣을 수 없기 때문에 Dictionary 클래스를 생성해야한다.
만약 Dictionary 클래스를 생성하는데 많은 비용(CPU, Memory)이 든다면 테스트하는게 비효율적이게 된다.
class SpellCheckerTest {
@Test
void isValid() {
assertTrue(SpellChecker.isValid("test"));
}
}
위와 같이 테스트를 할시에 SpellChecker 클래스에 있는 Dictionary 클래스 인스턴스를 매번 만들게 된다.
자원을 직접 명시한다는건
private static final Dictionary dictionary = new Dictionary(); // 자원을 직접 생성 == 자원을 직접 명시
위처럼 자원을 직접 생성한다는 뜻이다.
본인이 가지고있는 리소스에 따라 동작이 달라질 경우에는 의존성 주입 을 사용해야한다.
public class SpellChecker {
private final Dictionary dictionary = new Dictionary();
public static final SpellChecker INSTANCE = new SpellChecker();
private SpellChecker() {}
public boolean isValid(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
위처럼 싱글톤으로 작성해도 동일한 문제가 생긴다.
Dictionary 없이는 테스트하기가 어려워진다.
또한, 유연성과 재사용성이 떨어지게 된다.
Dictionary 가 바뀔때마다 SpellChecker 를 새로 만들어야 하기 때문이다.
public class SpellChecker {
private final Dictionary dictionary;
public SpellChecker(Dictionary dictionary) {
this.dictionary = dictionary;
}
public boolean isValid(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
의존성 주입을 통해 코드를 리펙토링 해보면 다음과 같다.
private final Dictionary dictionary;
public SpellChecker(Dictionary dictionary) {
this.dictionary = dictionary;
}
위의 부분이 의존성 주입부분이다.
어딘가에서 Dictionary 를 받을 수 있는 코드를 만들어 놓고
Dictionary 를 명시한 코드를 없앴다.
이렇게 되면 SpellChecker 의 모든 코드가 재사용이 가능해진다.
물론, Dictionary 인터페이스일 때라는 전제조건이 있다.
Dictionary 가 인터페이스가 아니라면 메서드에 대한 규약 이 없기때문에
SpellChecker 의 코드를 재사용하기가 불가능해진다.
public interface Dictionary {
boolean contains(String word);
List<String> closeWordsTo(String typo);
}
Dictionary 인터페이스를 만든다.
이렇게하면 SpellChecker 의 코드를 재사용할 수 있게된다.
어떤 형식의 Dictionary 의 구현체가 오더라도 메서드의 이름이 변경되지 않기 때문이다.
public class DefaultDictionary implements Dictionary{
@Override
public boolean contains(String word) {
return false;
}
@Override
public List<String> closeWordsTo(String typo) {
return null;
}
}
Dictionary 인터페이스를 구현한 DefaultDictionary 를 생성한다.
코드를 테스트하기도 간단해진다.
class SpellCheckerTest {
@Test
void isValid() {
SpellChecker spellChecker = new SpellChecker(new DefaultDictionary());
assertTrue(spellChecker.isValid("test"));
}
}
의존객체 주입을 사용한 클래스의 경우에는
이렇게 SpellChecker 클래스의 외부에서 SpellChecker 에서 사용할 객체를 주입하기 때문에
Mock 객체를 만들어 테스트 하기가 쉬워진다.
'개발 공부 > Java' 카테고리의 다른 글
이펙티브 자바 아이템 6 - 불필요한 객체 생성을 피하라 - 핵심 정리 (0) | 2022.09.22 |
---|---|
이펙티브 자바 아이템 5 - 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 - 완벽 공략 (1) | 2022.09.22 |
이펙티브 자바 아이템 4 - 인스턴스화를 막으려거든 private 생성자를 사용하라 - 핵심 정리 (0) | 2022.09.20 |
이펙티브 자바 아이템 3 - 생성자나 열거 타입으로 싱글턴임을 보증하라 - 완벽 공략 (0) | 2022.09.20 |
이펙티브 자바 아이템 3 - 생성자나 열거 타입으로 싱글턴임을 보증하라 - 핵심 정리 (0) | 2022.09.15 |
댓글