상태패턴

임종혁·2025년 1월 9일
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;
	int 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) {
			state = NO_QUARTER;
			System.out.println("동전이 반환 됩니다");
		} 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) {
			state = SOLD;
			System.out.println("손잡이를 돌리셨습니다");
		} 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() {
		if(state == HAS_QUARTER) {
			System.out.println("알맹이를 내보낼수 없습니다");
		} else if(state == NO_QUARTER) {
			System.out.println("동전을 넣어주세요");
		} else if(state == SOLD_OUT) {
			System.out.println("매진되었습니다. 다음 기회에 이용해 주세요");
		}else if(state == SOLD) {
			System.out.println("알맹이를 내보내고 있습니다.");
			count = count -1;
			if(count == 0) {
				System.out.println("더이상 알맹이가 없습니다");
				state = SOLD_OUT;
			}else {
				state = NO_QUARTER;
			}
		}
	}

}

다음과 같이 뽑기 기계 코드가 있을때

기능을 추가하기 위해서는 모든 메서드에 추가해야한다.

즉 확장성이 어렵다

새로운 디자인 구상하기

  1. 뽑기 기계와 관련된 모든 행동에 관한 메소드가 들어있는 State 인터페이스를 정의해아한다.
  2. 기계의 모든 상태 대상으로 상태 클래스를 구현한다
  3. 조건문 코드를 전부 없애고 상태 클래스에서 모든 작업을 위임한다.

class GumballMachine {
	

	
	State soldOutState;
	State noQuarterState;
	State hasQuarState;
	State soldState;
	
	State state = soldOutState;
	int count = 0;
	



	public GumballMachine(int numberGumballs) {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarState = new HasQuarterState(this);
		soldState = new SoldState(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();
	}
    
    public void dispense() {
		state.dispense();
	}
	
	void releasBall() {
		System.out.println("알맹이를 내보내고 있습니다.");
		if(count > 0 ) {
			count = count -1;
		}
	}

	public State getSoldOutState() {
		return soldOutState;
	}

	public void setSoldOutState(State soldOutState) {
		this.soldOutState = soldOutState;
	}

	public State getNoQuarterState() {
		return noQuarterState;
	}

	public void setNoQuarterState(State noQuarterState) {
		this.noQuarterState = noQuarterState;
	}

	public State getHasQuarState() {
		return hasQuarState;
	}

	public void setHasQuarState(State hasQuarState) {
		this.hasQuarState = hasQuarState;
	}

	public State getSoldState() {
		return soldState;
	}

	public void setSoldState(State soldState) {
		this.soldState = soldState;
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}
}
public class HasQuarterState implements State {
	GumballMachine gumballMachine;

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

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

	@Override
	public void turnCrank() {
		System.out.println("손잡이를 돌리셨습니다");
		gumballMachine.setState(gumballMachine.getSoldState());
	}

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

}
public class NoQuarterState implements State{
	
	GumballMachine gumballMachine;

	public NoQuarterState(GumballMachine gumballMachine) {
		this.gumballMachine = gumballMachine;
	}
	
	@Override
	public void insertQuarter() {
		System.out.println("동전을 넣었습니다");
		gumballMachine.setState(gumballMachine.getHasQuarState());
		
	}

	@Override
	public void ejectQuarter() {
		System.out.println("동전을 넣어주세요");
		
	}

	@Override
	public void turnCrank() {
		System.out.println("동전을 넣어주세요");
		
	}

	@Override
	public void dispense() {
		System.out.println("동전을 넣어주세요");
		
	}

}
public class SoldOutState implements State{
	
	GumballMachine gumballMachine;
	
	public SoldOutState(GumballMachine gumballMachine) {
		this.gumballMachine = gumballMachine;
	}

	
	@Override
	public void insertQuarter() {
		System.out.println("죄송합니다 매진되었습니다.");
		
	}

	@Override
	public void ejectQuarter() {
		System.out.println("동전을 반환할수 없습니다 ㄷ오전을 넣지 않았습니다");
		
	}

	@Override
	public void turnCrank() {
		System.out.println("죄송합니다 매진되었습니다");
		
	}

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


}
public class SoldState implements State{
	
	GumballMachine gumballMachine;
	
	@Override
	public void insertQuarter() {
		System.out.println("알맹이를 내보내고 있습니다.");
	}

	@Override
	public void ejectQuarter() {
		System.out.println("알맹이를 뽑으셨습니다");
		
	}

	@Override
	public void turnCrank() {
		System.out.println("손잡이는 한번만 돌려주세요");
		
	}

	@Override
	public void dispense() {
		gumballMachine.releasBall();
		if(gumballMachine.getCount() > 0) {
			gumballMachine.setState(gumballMachine.getNoQuarterState());
		}else {
			System.out.println("Oops, out of gumballs");
			gumballMachine.setState(gumballMachine.getSoldOutState());
		}
		
	}

}
  • 각상태의 행동을 별개의 클래스로 국지화
  • 관리하기 힙든 골치덩어리 if 선언문 없애기
  • 상태를 변경에는 닫혀 있게 하고 GumballMachine 클래스는 새로운 상태 클래스를 추가하는 확장에는 열려있음


상태 패턴의 정의


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

  • 실제로는 다른 클래스가 아닌 구성으로 여러 상태 객체를 바꿔가며 사용

상태 패턴 vs 전략 패턴

  • 상태패턴을 사용할때는 상태에 일련된 행동이 캡슐화 됨

  • 클라이언트는 상태객체를 몰라도 됨

  • 전략패턴을 사용할때 클라이언트가 context 객체에게 어떤 전략 객체를 사용할지 지정

  • 전략패턴은 서브클래스를 만드는 방법을 대신해서 유연성 극대화 하는 용도

  • conext 객체 수많은 조건문을 넣는 대신 상태 패턴을 사용한다 생각하면 됨

새로운 함수 winner 추가

public class WinnerState implements State {

	GumballMachine gumballMachine;

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

	@Override
	public void insertQuarter() {
		System.out.println("알맹이를 내보내고 있습니다.");
	}

	@Override
	public void ejectQuarter() {
		System.out.println("알맹이를 뽑으셨습니다");
		
	}

	@Override
	public void turnCrank() {
		System.out.println("손잡이는 한번만 돌려주세요");
		
	}

	@Override
	public void dispense() {
		gumballMachine.releasBall();
		if(gumballMachine.getCount() == 0) {
			gumballMachine.setState(gumballMachine.getSoldOutState());
		}else {
			gumballMachine.releasBall();
			System.out.println("축하드립니다 알맹이를 하나 더 받으실수 있습니다.");
			if(gumballMachine.getCount() >0) {
				gumballMachine.setState(gumballMachine.getNoQuarterState());
			} else {
				System.out.println("더이상 알맹이가 없습니다.");
				gumballMachine.setState(gumballMachine.getSoldOutState());
			}
		}
		
	}
	
	
}
public class HasQuarterState implements State {
	GumballMachine gumballMachine;
	Random randomWinner = new Random(System.currentTimeMillis());

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

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

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

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

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

}

0개의 댓글