클래스와 자원
• 클래스는 보통 하나 이상의 자원에 의존한다.
◦ 의존한다 = 관계성이 있다 / 자원이 클래스의 동작에 영향을 준다.
◦ 예 : 맞춤법 검사기는 사전 객체에 의존한다. (사전에 존재하는 단어가 아니면 밑줄)
하나 이상의 자원에 존재하는 클래스(1) : 맞춤법 검사기의 잘못된 구현 방식
public class SpellChecker {
private static final Lexicon dictionary = ... ;
private SpellChecker() {} // 객체 생성 방지용 생성자
public static boolean isValid(String word) { ... }
}
public class SpellChecker {
private final Lexicon dictionary = ... ;
public static SpellChecker INSTANCE = new SpellChecker();
private SpellChecker() {}
public static boolean isValid(String word) { ... }
}
• 위의 두가지 방식은 클래스가 단 하나의 사전 객체에만 의존한다고 가정하고 있다 BAD
◦ 일반적으로 사전은 하나로 구성되어 있지 않고, 여러개로 나누어져 있다. (전세계 언어 사전 X)
◦ 제안) final 한정자를 제거하고, 다른 사전으로 교체하는 메서드를 추가?
- 어색하고 오류를 내기 쉽다 + 멀티스레드 환경에서는 사용할 수 없다.
public class SpellChecker {
private static Lexicon dictionary = ...;
public static SpellChecker INSTANCE = new SpellChecker();
private SpellChecker() {}
public static boolean isValid(String word) { ... }
public static void changeDictionary(Lexicon new) { dictionary = new; }
}
의존 객체 주입 방법
• 해당 클래스의 인스턴스를 생성할 때 생성자에게 필요한 자원을 넘겨주는 방법
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary){
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean is Valid(String word) { ... }
}
public class KoreanDict implements Lexicon { ... }
public class EnglishDict implements Lexicon { ... }
Lexicon kordict = new KoreanDict();
Lexicon engdict = new EnglishDict();
SpellChecker korchecker = new SpellChecker(kordict);
SpellChecker engchecker = new SpellChecker(engdict);
korcheker.isVaild("한국말");
engcheker.isVaild("English");
• 불변성을 보장하기 때문에 여러 클래스가 같은 의존 객체들을 공유할 수 있다
◦ this.dictionary 로 받아서 사용했기 때문
• 정적 팩토리와 빌더에서도 이런 방식으로 의존 객체를 넘겨줄 수 있다.
응용 : 생성자에 자원 팩토리를 넘겨주는 방식 (팩토리 메서드 패턴)
• 팩토리 = 호출될 때 마다 특정 타입의 인스턴스를 만들어주는 객체
• Supplier<T> 인터페이스 = 팩토리의 완벽한 표현
◦ 함수형 인터페이스 : 매개변수는 없고, 반환값만 있다.
~~String supplier= new String();
supplier = "Hello World";~~
Supplier<String> supplier= () -> "Hello World";
String result = supplier.get();
System.out.println(result); // Hello World
• Supplier<T>를 입력으로 받는 메서드는 한정적 와일드카드 타입(bounded wildcard type)을 사용하여
팩터리의 타입 매개변수를 제한해야 한다.
◦ 와일드 카드 : 제네릭 코드에서 물음표(?) 로 표기되어 있는 것. 아직 알려지지 않은 타입 의미.
◦ 한정적 와일드카드 : (?) 가 무언가를 extend 한다.
◦ 명시한 타입의 하위 타입이라면 무엇이든 생성할 수 있는 팩토리를 넘길 수 있게 됨
Mosaic create(Supplier<? extends Tile> tileFactory) { ... }
의존 객체 주입 방식의 장점
• 코드의 유연성과 재사용성, 테스트의 용이성을 개선시킴
• 그러나 의존성이 많은 큰 프로젝트에서는 코드를 어지럽게 만들기도 함
◦ Spring 등의 의존 객체 주입 프레임워크를 사용하는 것이 좋음
- 의존 객체를 직접 주입하도록 설계된 API를 사용한다.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
• 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.
• 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라고 한다.
'Java' 카테고리의 다른 글
[Effective Java] ITEM9 : try-finally 보다는 try-with-resources를 사용하라 (0) | 2022.07.24 |
---|---|
[Effective Java] ITEM7 : 다 쓴 객체 참조를 해제하라 (0) | 2022.07.24 |
[Effective Java] ITEM3 : private 생성자 혹은 enum 타입으로 싱글톤임을 보증하라 (0) | 2022.07.23 |
[JAVA] GC(Garbage Collection) 톺아보기 (0) | 2022.07.10 |
[JAVA] JVM(Java Virtual Machine) 톺아보기 (0) | 2022.07.10 |