Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- Effetive Java
- @Valid
- springboot
- Firebase
- NotEmpty
- Proxy Patter
- restTemplate
- Item04
- SQL 삽입 공격
- deleteById
- @SpyBean
- 데이터베이스
- Web
- multi module
- Service Locator
- JPA
- 이펙티브 자바
- 트랜잭션
- Effective Java
- Connection Pool
- @ControllerAdvice
- Service Locator 패턴
- FCM
- 디자인 패턴
- java
- NotBlank
- @MockBean
- Spring Boot
- db
- 플라이웨이트
Archives
- Today
- Total
NoTimeForDawdling
Item05 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 본문
많은 클래스는 하나 이상의 자원에 의존합니다.
- 하나의 자원에 의존한다면 정적 유틸리티 클래스나, 싱글톤 방식을 써도 괜찮을 수 있습니다.
- 하지만 사용하는 자원에 따라 동작이 달라지는 클래스라면 정적 유틸리티 클래스나 싱글톤 방식이 적합하지 않습니다.
정적 유틸리티를 잘못 사용한 예
public class SpellChecker {
private static final Lexicon dictionary = new Lexicon(); // 특정 자원 명시
private SpellChecker() {} // 인스턴스화 방지
public static boolean isValid(String word) {...}
public static List<String> suggestions(String typo) {...}
}
싱글톤을 잘못 사용한 예
public class SpellChecker {
private final Lexicon dictionary = new Lexicon(); // 특정 자원 명시
private SpellChecker() {} // 인스턴스화 방지
public static SpellChecker2 INSTANCE = new SpellChecker2();
public static boolean isValid(String word) {...}
public static List<String> suggestions(String typo) {...}
}
위의 두 방식 모두 특정 사전(Lexicon)을 명시했기 때문에 단 하나의 사전(Lexicon)만 사용할 수 있습니다. 하지만 이 방법은 좋지 않습니다. 이유는 다음과 같습니다.
- 실제 사전은 언어별로 따로 있고, 특수 어휘용 사전이나 테스트용 사전도 필요할 수 있습니다.
- 사전 하나로 이 모든 케이스를 대응하기에는 힘듭니다.
final 한정자 제거 및 setter를 추가한다면?
- 그렇다면 final 한정자를 제거하고 사전(Lexicon)을 설정할 수 있는 setter를 추가한다면 괜찮을까요?
- 결론은 좋지 않습니다. 이 방식은 불변성이 보장되지 않아 오류를 내기 쉽고, 멀티스레드 환경에서는 사용할 수 없기 때문입니다.
해결 방법
위와 같은 상황에서는 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 의존 객체 주입 방식을 사용하면 됩니다.
public class SpellChecker {
private final Lexicon dictionary; // 특정 자원 명시하지 않음
public SpellChecker (Lexicon dictionary) { // 생성자를 통해 원하는 객체 주입
this.dictionary = Objects.requireNonNull(dictionary);
}
public static boolean isValid(String word) {...}
public static List<String> suggestions(String typo) {...}
}
위 클래스는 맞춤법 검사기(SpellChecker)를 생성할 때 의존 객체인 사전(Lexicon)을 주입해 줍니다.
- 자원이 몇 개든 의존 관계가 어떻든 상관없이 잘 동작하며, 여러 클라이언트가 의존 객체들을 안심하고 공유할 수 있습니다. (final로 불변성을 보장해 주기 때문에)
- 의존 객체 주입 패턴의 변형으로, 생성자에 자원 팩토리를 넘겨주는 방식이 있습니다.
- 한마디로 *팩토리 메서드 패턴을 구현한 것입니다.
팩토리 메서드 패턴(Factory Method pattern)
- 팩토리 메서드 패턴이란 객체 생성 처리를 서브 클래스로 분리 해 처리하도록 캡슐화하는 패턴입니다.
- 팩토리 메서드 패턴은 객체의 생성 코드를 별도의 클래스/메서드로 분리함으로써 객체 생성의 변화에 대비하는데 유용합니다.
- 특정 기능의 구현은 개별 클래스를 통해 제공되는 것이 좋습니다. 이유는 다음과 같습니다.
- 기능의 변경이나 상황에 따른 기능의 선택은 해당 객체를 생성하는 코드의 변경을 초래합니다.
- 상황에 따라 적절한 객체를 생성하는 코드는 자주 중복될 수 있습니다.
- 객체 생성 방식의 변화는 해당되는 모든 코드 부분을 변경해야 하는 문제가 발생합니다.
다음 코드는 클라이언트가 제공한 팩토리가 생성한 타일(Tile)로 구성된 모자이크(Mosaic)를 만드는 메서드입니다.
Mosaic create(Supplier<? extends Tile> tileFactory) {...}
클라이언트는 타일(Tile)을 생성하는 Supplier를 정의한 후 인자로 넘겨주면, create 메서드에서 넘겨받은 Supplier로부터 생성되는 타일(Tile)을 가지고 모자이크(Mosaic)를 만들 수 있게 됩니다.
정리
- 클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 정적 유틸리티 클래스와 싱글턴은 사용하지 않는 것이 좋습니다. 또한, 이 자원들을 클래스가 직접 만들게 해서도 안됩니다.
- 필요한 자원을 생성자에 넘겨주어(의존 객체 주입) 클래스의 유연성, 재사용성, 테스트 용이성을 개선할 수 있습니다.
'Effective Java' 카테고리의 다른 글
Item07 다 쓴 객체 참조를 해제하라 (0) | 2021.06.10 |
---|---|
Item06 불필요한 객체 생성을 피하라 (0) | 2021.05.27 |
Item04 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.03.31 |
Item03 private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2021.03.09 |
Item02 생성자 매개변수가 많은 경우에 빌더 사용을 고려해 볼 것 (0) | 2021.03.04 |