Study/Effective-Java
[item#05] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
728x90
반응형
IOC(Inversion of Controll), DI(dependency injection)
- 사용하는 자원에 따라 동작이 달라지는 클래스는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.
- 의존 객체 주입이란 인스턴스를 생성할 때 필요한 자원을 넘겨주는 방식이다.
- 의존 객체 주입을 사용하면 클래스의 유연성, 재사용성, 테스트 용이성을 개선할 수 있다.
==================================================================================================
public class SpellChecker {
private static final Dictionary dictionary = new Dictionary();
private SpellChecker() {
}
public static boolean isVaild(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public static List<String> suggestions(String typo) {
// TODO 여기 SpellChecker 코드
return dictionary.closeWordsTo(typo);
}
}
위와 같은 Utils성 Class의 문제.
- Dictionary를 변경 불가(한국어 사전인지, 영어사전인지 등...)
- Dictionary를 생성하는데 많은 비용이 든다고 가정하에, SpellChecker만의 코드를 test할 때 필요없는 자원 필요
@Test void isVaild() { assertTrue(SpellChecker.isVaild("test")); }
- 유연하지 않고 test코드 작성 시 불편
- Utils성이 아니라 Singleton으로 작성시에도 마찬가지. (유연성 재사용성 떨어짐)
DI를 사용한 코드
public class SpellChecker {
private final Dictionary dictionary; // Dictionary가 인터페이스일 때 유연, 재사용성
public SpellChecker(Dictionary dictionary) {
this.dictionary = dictionary;
}
public boolean isVaild(String word) {
// TODO 여기 SpellChecker 코드
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
return dictionary.closeWordsTo(typo);
}
}
public interface Dictionary {
boolean contains(String words);
public List<String> closeWordsTo(String typo);
}
장점 1. Dictionary를 언제든지 갈아 끼울 수 있는 유연함.
장점 2. Mock Dictionary를 만들어 test의 용의함.
@Test
void isVaild() {
SpellChecker spellChecker = new SpellChecker(new DefaultDictionary());
assertTrue(spellChecker.isVaild("test"));
}
DI 패턴의 변형
- DI 변형으로 생성자에 자원 팩터리를 넘겨줄 수 있다.
- Supplier
인터페이스가 팩터리를 표현한 완벽한 예 - 한정적 와일드카드 타입을 사용해 팩터리의 타입 매개변수를 제한해야 한다. => Dictionary를 인터페이스로 구현하면 됨
- 의존 객체가 많은 경우에 스프링 같은 의존 객체 주입 프레임워크 도입을 고려할 수 있다.
public SpellChecker(DictionaryFactory dictionaryFactory) {
this.dictionary = dictionaryFactory.get();
}
자원을 바로 받는게 아니라 자원을 만들어주는 팩터리를 받아서 중간단계를 조금 더 추상화 한 방식
Dictionary가 만들어지는 방식이 복잡한 경우에 사용할 수 있다.
public SpellChecker(Supplier<Dictionary> dictionarySupplier) {
this.dictionary = dictionarySupplier.get();
}
SpellChecker spellChecker = new SpellChecker(DefaultDictionary::new);
SpellChecker spellChecker = new SpellChecker(DictionaryFactory::get2);
BeanFactory또는 ApplicationContext
- Inversion of Control - 뒤짚힌 제어권
- 자기 코드에 대한 제어권을 자기 자신이 가지고 있지 않고 외부에서 제어하는 경우
- 제어권? 인스턴스를 만들거나, 어떤 메소드를 실행하거나, 필요로하는 의존성을 주입 받는등...
- 스프링 IOC 컨테이너 사용 장점
- 수많은 개발자에게 검증되었으며 자바 표준 스팩(@Inject)도 지원
- 손쉽게 싱글톤 Scope을 사용 가능
- 객체 생성(Bean) 관련 라이플 사이클 인터페이스 제공
스프링 - 비 침투적인(프레임워크가 제공하는 코드가 노출되는 코드 - 침투적인 코드), POJO를 유지.
IOC코드 등은 패스...
팩토리 메서드 패턴
https://dev-youngjun.tistory.com/195
Product Interface
public interface Dictionary {
boolean contains(String words);
public List<String> closeWordsTo(String typo);
}
Creator Interface
public interface DictionaryFactory {
Dictionary getDictionary();
}
ConcreteCreator
public class DefaultDictionaryFactory implements DictionaryFactory {
@Override
public Dictionary getDictionary() {
return new DefaultDictionary();
}
}
사용
public class SpellChcker {
private Dictionary dictionary; // product interface
// public SpellChcker(DictionaryFactory dictionaryFactory) {
// this.dictionary = dictionaryFactory.getDictionary();
// }
public SpellChcker(Supplier<DictionaryFactory> dictionarySupplier) {
this.dictionary = dictionarySupplier.get().getDictionary();
}
public static void main(String[] args) {
SpellChcker chcker = new SpellChcker(DefaultDictionaryFactory::new);
SpellChcker chcker2 = new SpellChcker(MockDictionaryFactory::new);
}
}
728x90
반응형
'Study > Effective-Java' 카테고리의 다른 글
[item#07] 다 쓴 객체 참조를 해제하라. (1) | 2023.11.27 |
---|---|
[item#06] 불필요한 객체 생성을 피하라 (2) | 2023.11.27 |
[item#03] private 생성자나 열거타입으로 싱글턴임을 보증하라 (1) | 2023.11.27 |
[item#02] 정적 팩터리와 생성자에 선택적 매개변수가 많을 때 고려할 수 있는 방안 (1) | 2023.11.27 |
[item#01] 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2023.11.27 |
댓글