상태 패턴

이진호·2022년 10월 16일
0

GoF 디자인 패턴

목록 보기
3/3
post-thumbnail

상태 패턴(State Pattern)

상태 패턴(State Pattern)을 사용하면 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.

상태 패턴(State Pattern)

  • Context의 상태 클래스 전환을 반드시 구성 상태 클래스에서 할 필요는 없음. 단 상태 전환이 고정되어 있는 경우에만 가능하며, 상태 전환이 동적으로 결정되는 경우라면 상태 클래스 내에서 처리하는 것이 좋음.
    • 상태 클래스 내에서 처리하는 경우 Context가 변경에 닫혀 있게 됨.
    • 반대로 Context에서 처리하는 경우, 상태 클래스가 변경에 닫혀 있게 됨.
  • 클라이언트에서 상태 객체를 직접 호출하는 경우는 없음. 상태를 관리하는 일은 전적으로 Context 책임.
  • Context의 인스턴스가 아주 많은 경우, 각 상태 객체를 정적 인스턴스 변수에 할당, 여러 Context에서 상태 객체를 공유할 수도 있음. 이때 상태 객체에서 Context에 있는 메소드 또는 인스턴스 변수를 써야 하는 경우, handle() 메소드에 Context 객체의 레퍼런스를 전달해야 함.
  • 아래 예제에서는 State를 인터페이스로 선언했지만, 추상 클래스로 선언해도 됨.

예제

public class GumballMachine {
	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
	State winnerState;

	State state;
	int count = 0;

	public GumballMachine(int numberGumballs) {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		winnerState = new WinnerState(this);

		this.count = numberGumballs;
		if (numberGumballs > 0) {
			state = noQuarterState;
		} else {
			state = soldOutState;
		}
	}

	public void insertQuarter() {
		state.insertQuarter();
	}

	public void ejectQuarter() {
		state.ejectQuarter();
	}

	public void turnCrank() {
		state.turnCrank();
		if (state == soldState || state == winnerState) {
			state.dispense();
		}
	}
    
	public void refill(int count) {
		this.count += count;
		state.refill();
	}
    
	void releaseBall() {
		System.out.println("알맹이를 내보내고 있습니다.");
		if (count > 0) {
			count = count - 1;
		}
	}
}
public interface State {
	void insertQuarter();

	void ejectQuarter();

	void turnCrank();

	void dispense();

	void refill();
}

// 각 State 구현체에서 다른 세부 로직 구현
public class HasQuarterState implements State {
	Random randomWinner = new Random(System.currentTimeMillis());
	GumballMachine gumballMachine;

	public HasQuarterState(GumballMachine gumballMachine) {
		this.gumballMachine = gumballMachine;
	}

	@Override
	public void insertQuarter() {
		System.out.println("동전은 한 개만 넣어주세요.");
	}

	@Override
	public void ejectQuarter() {
		System.out.println("동전이 반환됩니다.");
		this.gumballMachine.setState(gumballMachine.getNoQuarterState());
	}

	@Override
	public void turnCrank() {
		System.out.println("손잡이를 돌리셨습니다.");
		int winner = randomWinner.nextInt(10);
		if ((winner ==	 0) && (gumballMachine.getCount() > 1)) {
			this.gumballMachine.setState(gumballMachine.getWinnerState());
		} else {
			this.gumballMachine.setState(gumballMachine.getSoldState());
		}
	}

	@Override
	public void dispense() {
		System.out.println("알맹이를 내보낼 수 없습니다.");
	}

	@Override
	public void refill() {
		System.out.println("알맹이가 리필되었습니다.");
	}
}

상태 패턴과 전략 패턴의 차이

상태 패턴과 전략 패턴의 다이어그램은 똑같으나, 사용 용도가 다름. 상태 패턴과 전략 패턴 모두 Context 객체 내부의 구성 객체(상태 객체/전략 객체)에 일련의 행동이 캡슐화 되고, 상황에 따라 구성 객체를 선택해서 호출함.

상태 패턴에서는 Context 객체의 내부 상태에 따라 현재 상태를 나타내는 상태 객체가 바뀜, 클라이언트는 상태 객체를 몰라도 됨.

전략 패턴에서는 클라이언트가 Context 객체에게 어떤 전략 객체를 사용할 직접 지정함.

출처

0개의 댓글