
블랙잭 미션에서 플레이어나 딜러는 특정한 상태를 가진다.
예를 들어, 2장을 뽑는 시작 상태, 카드를 뽑는 Hit 상태, 카드 뽑기를 멈춘 Stand 상태, 2장이 21점인 Blackjack 상태, 점수가 21점을 초과한 버스트 상태가 있다.
bust 상태에서는 draw를 못한다. 또는 hit 상태에서는 draw를 할 수 있다.
이 경우 if문이 굉장히 많이 생기게 된다.
반복되는 조건물을 줄이기 위햇 많이 사용하는 방법은 다형성을 이용하는 것이다.
객체가 특정 상태에 따라 행위를 달리하는 경우, 조건문을 통해서 행위를 다르게 하는게 아닌 상태를 객체화해서 상태가 행동할 수 있도록 위임하는 패턴이다.
우선 interface나 abstract를 만들어 줍니다.
public abstract class State {
protected Hand hand;
protected State(Hand hand) {
this.hand = hand;
}
public abstract State draw(Deck deck);
public abstract State stay();
public abstract boolean isFinished();
public int calculateScore() {
return hand.calculateScore();
}
public List<Card> getCards() {
return hand.getCards();
}
public boolean isBlackjack() {
return hand.isBlackjack();
}
}
State를 상속한 다양한 상태들을 만들어 줍니다.
여기서 hit인 경우는 다음 상태로 Bust, Hit으로 갈 수 있습니다.
하지만 BustState에서는 draw를 할 수 없습니다.
public class HitState extends State {
protected RunningState(Hand hand) {
super(hand);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public State draw(Deck deck) {
hand = hand.add(deck.draw());
if (hand.isBust()) {
return new BustState(hand);
}
return new HitState(hand);
}
@Override
public State stay() {
return new StandState(hand);
}
}
public class BustState extends State {
protected FinishedState(Hand hand) {
super(hand);
}
@Override
public State draw(Deck deck) {
throw new UnsupportedOperationException("이미 종료된 상태입니다.");
}
@Override
public State stay() {
throw new UnsupportedOperationException("이미 종료된 상태입니다.");
}
@Override
public boolean isFinished() {
return true;
}
}
이렇게 상태에 따라 할 수 공통으로 정의된 메서스의 행동이 다르게 사용됩니다.
현재 무슨 상태인지 알기 위해서 instance of 를 사용하는 경우가 있다. 하지만 이것은 성능상 문제점이 있기도 하고, OCP, SRP 위반이므로 사용하기에는 별로이다.
상태가 많이 보이는 경우 조건문을 이용하지 말고 상태를 객체화해서 해결해보자!