의존
어떤 요소 A가 동작하기 위해서 다른 요소 B가 필요하다면, A가 B를 의존한다.
public class Ladder {
private final List<Line> lines;
Ladder() {
this.lines = new ArrayList<>();
}
}
Java
복사
Ladder가 동작하기 위해서는 List<Line> 이 필요하다. 즉, Laddder는 List와 Line을 의존한다.
정말 간단하게 자바에서 import 하는 모든 것을 의존한다고 보면 된다. 단, 그게 전부는 아니다.
이하의 글에서 A 를 의존을 하는 쪽, B를 의존 대상 이라 표현한다.
주입
주입의 사전적 의미는 다음과 같다.
어떤 물체 안에 액체나 기체 따위를 집어넣음.
- 네이버 국어사전
어떤 대상의 외부에서, 내부로 무언가를 집어 넣는 것!
의존성 주입
의존을 하는 것이 의존 대상이 되는 것을 직접 만들지 않고, 외부에서 의존 대상을 집어 넣는 것!
조금 더 쉬운 이해를 위해 코드로 살펴 보자
public class Ladder {
private final List<Line> lines;
Ladder() {
this.lines = new ArrayList<>();
// 생략
}
// 생략
}
Java
복사
의존성 주입을 사용하지 않은 코드
public class Ladder {
private final List<Line> lines;
Ladder(List<Line> lines) {
this.lines = lines;
}
// 생략
}
Java
복사
의존성 주입을 사용한 코드
의존성 주입을 사용하지 않은 코드에서는 의존 대상을 직접 생성했다. 반면, 의존성 주입을 사용한 코드는 의존 대상을 생성자를 통해 외부에서 받아왔다.
의존성 주입의 장점과 주의할 점
장점
1.
의존을 하는 쪽을 의존 대상의 변경으로부터 안전하게 보호할 수 있다.
2.
의존 대상을 다른 것으로 변경할 수 있다.
3.
테스트를 쉽게 할 수 있다.
주의할 점
의존을 하는 쪽 입장에서 의존 대상을 구현 세부 사항으로 바라볼 수 있다.
의존성 주입의 사용 예시
Stream API
private Piece findPiece(Position from) {
return piecesOnBoard.stream()
.filter(piece -> piece.isOn(from))
.findFirst().orElseThrow();
}
Java
복사
.filter(piece -> piece.isOn(from)) 이 부분은 스트림 외부에서 Piece 를 boolean 으로 바꾸는 방법을 주입 받는 것이다. 즉, 의존성 주입이다.
블랙잭
카드를 뽑는 방법을 선택할 수 있는 Deck
Deck 은 카드 뭉치를 추상화 한 것으로, 카드를 선택하는 전략을 외부에서 주입받아 카드 한 장을 반환한다.
public class Deck {
private final List<Card> cards;
public Deck(List<Card> cards) {
this.cards = new ArrayList<>(cards);
}
public Card draw(CardSelectStrategy cardSelectStrategy) {
if (cards.isEmpty()) {
throw new IllegalArgumentException("덱이 비어있습니다.");
}
Card card = cardSelectStrategy.select(cards);
cards.remove(card);
return card;
}
}
Java
복사
전략을 주입한 Deck
이를 통해 Deck이 제대로 동작하는지 테스트를 할 수 있게 되었다.
class DeckTest {
private static final FirstCardSelectStrategy FIRST_CARD_SELECT_STRATEGY = new FirstCardSelectStrategy();
@Test
@DisplayName("원하는 방식대로 카드가 뽑히는지 검증")
void validateDraw() {
Deck deck = Deck.of(ACE_HEART);
Card card = deck.draw(FIRST_CARD_SELECT_STRATEGY);
Assertions.assertThat(card)
.isEqualTo(ACE_HEART);
}
}
Java
복사
의존성 주입을 사용해 테스트가 쉬운 Deck
Deck의 구현 세부 사항을 아는 Player
Player 는 블랙잭 게임의 참가자 중 플레이어를 추상화 한 것으로, 카드 뭉치에서 카드 한장을 뽑는 행동을 한다.
public class Player {
private final String name;
private final BettingMoney bettingMoney;
public DrawResult draw(Deck deck, CardSelectStrategy cardSelectStrategy) {
return deck.draw(cardSelectStrategy);
}
}
Java
복사
카드를 뽑는 전략을 주입받아 덱에서 카드를 선택하는 Player
덱에서 어떤 카드가 선택되는지는 덱의 세부 구현사항 아닌가? 플레이어가 이를 선택하는 것이 옳은가?
두마리의 토끼를 모두 잡은 Deck
이 경우는 의존성 주입을 사용하되, 패키지 내부에서만 사용하는 방법을 채택할 수 있다.
public class Deck {
private final List<Card> cards;
public Deck(List<Card> cards) {
this.cards = new ArrayList<>(cards);
}
public Card draw() {
return draw(RandomCardSelectStrategy.INSTANCE);
}
Card draw(CardSelectStrategy cardSelectStrategy) {
if (cards.isEmpty()) {
throw new IllegalArgumentException("덱이 비어있습니다.");
}
Card card = cardSelectStrategy.select(cards);
cards.remove(card);
return card;
}
}
Java
복사