Java/Design Pattern

[헤드퍼스트 디자인 패턴] 10. 상태 패턴

noahkim_ 2024. 12. 18. 22:07

에릭 프리먼 님의 "헤드퍼스트 디자인 패턴" 책을 정리한 포스팅 입니다


1. 상태 기계

상태를 모음
  • 상태를 표현하는 인스턴스 변수 정의

 

행동을 모음

 

GumballMachine

public class GumballMachine {
    final static int SOLD_OUT = 0;
    final static int NO_QUARTER = 1;
    final static int HAS_QUARTER = 2;
    final static int SOLD = 3;    
    
    int state = SOLD_OUT, count = 0;
    
    public GumballMachine(int count) {
    	this.count = count;
        if (count > 0) state = NO_QUARTER;
    }
    
    public void insertQuarter() {
    	if (state == HAS_QUARTER) {
            System.out.println("동전은 한 개만 넣어 주세요");
        } else if (state == NO_QUARTER) {
            state = HAS_QUARTER;
            System.out.println("동전을 넣으셨습니다.");
        } else if (state == SOLD_OUT) {
            System.out.println("매진 되었습니다.");
        } else if (state == SOLD) {
            System.out.println("알맹이를 내보내고 있습니다.");
        } 
    }
    
    public void ejectQuarter() {
    	if (state == HAS_QUARTER) {
            System.out.println("동전이 반환됩니다");
            state = NO_QUARTER;            
        } else if (state == NO_QUARTER) {
            System.out.println("동전을 넣어주세요.");
        } else if (state == SOLD_OUT) {
            System.out.println("동전을 넣지 않았습니다.");
        } else if (state == SOLD) {
            System.out.println("이미 알맹이를 뽑으셨습니다.");
        } 
    }
    
    public void turnCrank() {
    	if (state == HAS_QUARTER) {
            System.out.println("손잡이를 돌리셨습니다.");
            state = SOLD;
            dispense();
        } else if (state == NO_QUARTER) {
            System.out.println("동전을 넣어주세요.");
        } else if (state == SOLD_OUT) {
            System.out.println("매진되었습니다.");
        } else if (state == SOLD) {
            System.out.println("손잡이는 한번만 돌려주세요.");
        } 
    }      
    
    public void dispense() {
        System.out.println("알맹이를 내보내고 있습니다.");
        if (--count == 0) {
            System.out.println("더 이상 알맹이가 없습니다.");
            state = SOLD_OUT;
        } else {
            state = NO_QUARTER;
        }
    }
}
  • 만약 상태가 추가될 경우, 상태에 의존하는 모든 코드들에 분기 코드가 추가되어야 함

 

3. 상태 패턴

  • 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있음

 

vs 전략패턴
  • 클라이언트가 상태 객체를 몰라도 됨
    • 전략패턴은 보통 클라이언트가 전략패턴에 대한 객체를 전달해줌

 

State

public interface State {
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}
public class NoQuarterState implements State {
    GumballMachine gumballMachine;
    
    public NoQuarterState(GumballMachine gumballMachine) {
    	this.gumballMachine = gumballMachine;
    }
    
    public void insertQuarter() {
    	System.out.println("동전을 넣으셨습니다.");
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }
    
    public void ejectQuarter() { System.out.println("동전을 넣어주세요."); }
    public void turnCrank() { System.out.println("동전을 넣어주세요."); }
    public void dispense() { System.out.println("동전을 넣어주세요."); }
}
  • 상태를 표현할 인터페이스 정의
  • 상태를 표현하는 구현체들을 모두 구현

 

GumballMachine

 

public class GumballMachine {
    State soldOutState, noQuarterState, hasQuarterState, soldState, state;
    int count;
    
    public GumballMachine(int numberGumballs) {
    	soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
    	    	
        this.count = numberGumballs;        
        state = count > 0 ? noQuarterState : soldOutState;
    }
    
    public void insertQuarter() {
    	state.insertQuarter();
    }
    
    public void ejectQuarter() {
    	state.ejectQuarter();
    }
    
    public void turnCrank() {
    	state.turnCrank();
        state.dispense();
    }      
    
    public void releaseBall() {
        System.out.println("알맹이를 내보내고 있습니다.");
        if (count > 0) count--;
    }
}​