상태 패턴이란?
객체지향 디자인 패턴 중 하나로 객체의 상태에 따라 다르게 동작해야 되는 경우가 빈번할 경우 사용할 수 있는 패턴이다.
예시
자판기 객체를 구현한다고 해보자. 자판기에는 코인을 넣고 음료를 고를 수 있는 기능이 있다. 코인을 넣을 때는 코인이 없는 상태였다면 코인이 있는 상태로 수정해야되고, 코인이 있는 상태였으면 코인 개수를 단순히 증가 시킨다. 음료를 고르는 경우에는 코인이 있는 상태면 음료를 고르고 코인의 개수를 하나 줄인 다음 다시 상태를 갱신 시켜야된다. 코인이 없는 경우는 음료를 선택해도 아무 일도 일어나지 않는다.
상태 패턴을 사용하지 않는 경우
public class VendingMachine {
public static enum State {NO_COIN, SELECTABLE}
private State state = State.NO_COIN;
public void insertCoin(int coin) {
switch(state) {
case NO_COIN:
increaseCoin(coin);
state = State.SELECTABLE;
break;
case SELECTABLE:
increaseCoin(coin);
}
}
public void selectDrink(int productId) {
switch(state) {
case NO_COIN:
//아무 것도 하지 않음
break;
case SELECTABLE:
provideProduct(productId);
decreaseCoin();
if (hasNocoin()) {
state = State.NO_COIN;
}
}
}
}
위와 같이 조건문의 나열로 처리하게 되어서 메소드의 길이가 길어진다. 이는 코드의 가독성을 떨어뜨린다.
상태 패턴을 사용한 경우
public class VendingMachine {
private State state;
public VendingMachine() {
state = new NoCoinState();
}
public void insertCoin(int coin, this) {
state.increaseCoin(coin, this);
}
public void selectDrink(int productId) {
state.select(productId, this);
}
public void changeState(State state) {
this.state = state;
}
}
interface State {
void increaseCoin(int coin, VendingMachine vendingMachine);
void select(int productId, VendingMachine vendingMachine);
}
public class NoCoinState implements State {
@Override
public void increaseCoin(int coin, VendingMachine vendingMachine) {
vendingMachine.insertCoin(coin);
vendingMachine.changeState(new SelectableState());
}
@Override
public void select(int productId, VendingMachine vendingMachine) {
SoundUtil.beep();
}
}
public class SelectableState implements State {
@Override
public void increaseCoin(int coin, VendingMachine vendingMachine) {
vendingMachine.insertCoin(coin);
}
@Override
public void select(int productId, VendingMachine vendingMachine) {
vendingMachine.provideProduct(productId);
vendingMachine.decreaseCoin();
if (vendingMachine.hasNoCoin()) {
vendingMachine.changeState(new NoCoinState());
}
}
}
위와 같이 조건문이 사라지고 상태 별로 필요한 로직만 볼 수 있기 때문에 가독성이 좋아진다.
상태 변경은 누가?
상태 패턴을 적용할 때 고려할 문제는 콘텍스트의 상태 변경을 누가 하느냐에 대한 것이다. 두 가지 방법이 있다.
- 각 상태 객체에서 콘텍스트의 상태를 변경해주는 방법
public class NoCoinState implements State {
@Override
public void increaseCoin(int coin, VendingMachine vendingMachine) {
vendingMachine.insertCoin(coin);
// 상태 객체에서 콘텍스트의 상태 변경
vendingMachine.changeState(new SelectableState());
}
}
- 콘텍스트에서 상태를 변경하는 방법
public class VendingMachine {
public void insertCoin(int coin, this) {
state.increaseCoin(coin, this);
if (hasCoin()) {
// 콘텍스트에서 상태 변경
changeState(new SelectableState());
}
}
}
참고 자료
최범균 (2013). 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴
댓글남기기