-
[디자인 패턴] 7. 스테이트 패턴 (State Design Pattern)Computer Science/Design Pattern 2022. 10. 14. 02:09728x90
스테이트 패턴 (State Pattern)
객체가 상태에 따라 행위를 다르게 할 때, 직접 상태를 체크하여 상태에 따른 행위를 호출하는 것이 아닌,
상태를 객체화하여 필요에 따라 다르게 행동하도록 위임하는 디자인 패턴
- 객체의 특정 상태 = 클래스 : 객체가 시스템에 존재하는 동안 (객체 라이프타임) 객체가 가질 수 있는 조건이나 상황
- 상태에 따른 행위 (Action) = 클래스 내 메서드
- 상태 클래스를 인터페이스로 캡슐화
상태 머신 다이어그램
선풍기 예시
Event (parameter조건) / Action() : 이벤트 조건 만족 시, Action() 메소드 실행
- 선풍기는 기본적으로 OFF 상태
- OFF 상태에서 사용자가 스위치 켜면 switch_on 이벤트 발생. 전원 들어온 상태라면 power_exists 조건 ON 상태로 진입. 이때 turnon() 액션을 실행
- OFF 상태에서 사용자가 스위치 켜면 switch_on 이벤트 발생. 전원 들어오지 않은 상태라면 power_exists 조건 OFF 상태 유지 머무름
- 사용자가 ON 상태에서 동작 버튼 누ㄴ르면 run 이벤트 발생 WORKING 상태로 진입. operate() 액션을 실행
- 선풍기가 ON 상태나 WORKING 상태에 머무를 때 사용자가 스위치를 끄면 switch_off 이벤트가 발생하고 이 이벤트로 인해 OFF 상태로 진입.
복합 상태 (Composite state)
- Active 복합상태 에서는 ON 상태나 WORKING 중 어떤 상태에 있던 switch_off 이벤트 발생 시 OFF 상태로 진입한다.
- 복합 상태는 동일한 진입으로 인한 상태 머신의 복잡성을 줄일 수 있다.
- 복합 상태에서도 시작 상태 존재
새로운 상태 추가
형광등 예시
- 형광등 ON (켜짐) -> 이미 켜진 상태에서 ON 누르면 변화 X
- 켜져 있을 때 OFF 누르면 꺼짐 -> 꺼진 상태에서 OFF 누르면 변화 X
문제점
취침등 상태 추가
취침등 (SLEEPING 상태 추가) - 각 상태에 if, case 문을 활용한 코드 추가 확장.
- 상태가 복잡해 질 경우, 조건문 상태변화가 어떻게 이루어지는지 파악 어려움.
- 새로운 상태가 계속 추가될 경우 모든 메소드의 if문에 반영되어야 하기 때문에 빈번한 코드 수정 발생
무엇이 변하는 부분을 찾고, 해당 부분을 캡슐화하여, 시스템이 어떤 상태에 있는지 상관없이 상태가 변하더라도 독립직이 되도록 수정 필요.
해결책
상태를 캡슐화 (스테이트 패턴) - 스트래티지 전략 패턴과 비슷하다.
- 구체적인 상태를 참조하는것이 아님
- Light 클래스는 시스템이 어떤 상태에 있는지 무관하게 된다. (독립적)
정리
- 현실세계 많은 객체는 자신이 처한 상태에 따라 일을 다르게 수행
- 이를 표현하는 직관적인 방법은, 상태에 따라 상태 하나하나가 어떤 상태인지 검사하여 이를 다르게 수행
- if, switch문 이용 구현 (복잡함, 코드 수정 어려움)
- 스테이트 패턴 이용
- 어떤 행위를 수행할 때 상태 행위를 수행하도록 이임하는 것
- 스테이터스 패턴에서는 상태를 클래스로 분리하고 수행하는 행위들을 메소드로 구현
- 이러한 상태들을 외부로부터 캡슐화 하도록 인터페이스 활용
- 시스템에 각 상태를 나타내는 하위 클래스로 하여금 실체화한다.
실습 예제
문방구 뽑기 기계
문방구 뽑기기계 상태머신 다이어그램 - 상태 정의 : 동전 있음, 동전 없음, 알맹이 없음, 알맹이 판매
- 변수 : 동전 없음 = 0, 동전 있음 = 1, 알맹이 없음 = 2, 알맹이 판매 = 3, int state = 현재 상태
- 동전 투입, 동전 반환, 손잡이 돌림, 알맹이 내보냄 [ball exist] 알맹이 존재에 따라 상태가 달라진다.
1. if문을 이용한 구현
public class BallMachine { //각 상태 정의 (변하지 않으므로 final로 정의) final static int SOLD_OUT = 0; final static int NO_COIN = 1; final static int HAS_COIN = 2; final static int SOLD = 3; //현재 상태 초기화 int state = SOLD_OUT; int count = 0; // 볼의 개수 //초기 볼의 개수를 인자로 받아들이는 생성자 public BallMachine(int count) { this.count = count; //인자로 받은 볼의 개수 저장 //알맹이 개수가 0이 아니면 동전을 누군가가 넣어주길 기다리고 있는 NO_COIN 상태로 전환됨 //알맹이 개수가 0이면 그냥 SOLD_OUT 상태에 머무름 if (count > 0) { state = NO_COIN; } } //동전이 투입되는 경우 public void insertCoin() { //동전이 이미 있을 때 if (state == HAS_COIN) { System.out.println("동전은 한개만 넣어주세요."); //동전이 투입된 경우 }//동전이 투입된 경우 else if (state == NO_COIN) { state = HAS_COIN; System.out.println("동전을 넣으셨습니다."); }//매진 상태인 경우 else if (state == SOLD_OUT) { System.out.println("매진되었습니다."); } else if (state == SOLD) { System.out.println("볼이 나오고 있습니다."); } } //동전이 반환되는 경우 public void ejectCoinI() { //동전이 이미 있을 때 if (state == HAS_COIN) { System.out.println("동전을 반환합니다."); state = NO_COIN; }//동전이 투입된 경우 else if (state == NO_COIN) { System.out.println("동전을 넣어 주세요."); }//매진 상태인 경우 else if (state == SOLD_OUT) { System.out.println("동전을 넣지 않으셨습니다."); //매진상태에선 동전 투입할 수 없고 반환할 수도 없음 } else if (state == SOLD) { System.out.println("이미 볼을 뽑으셨습니다."); } } //손잡이를 돌리는 경우 public void turnCrank() { //동전이 이미 있을 때 if (state == HAS_COIN) { System.out.println("손잡이를 돌리셨습니다."); state = SOLD; //볼 내보내기 수행 dispense(); }//동전이 투입된 경우 else if (state == NO_COIN) { System.out.println("동전을 넣어 주세요."); }//매진 상태인 경우 else if (state == SOLD_OUT) { System.out.println("매진되었습니다."); } else if (state == SOLD) { System.out.println("손잡이는 한 번만 돌려주세요."); } } //볼 내보내기 public void dispense() { //나오면 안되는 오류 상황 if (state == HAS_COIN) { System.out.println("볼을 내보낼 수 없습니다."); }//나오면 안되는 오류 상황 else if (state == NO_COIN) { 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_COIN; } } @Override public String toString() { return "BallMachine [state=" + state + ", count=" + count + "]"; } }
테스트
public class BallMachineTest { // final static int SOLD_OUT = 0; // final static int NO_COIN = 1; // final static int HAS_COIN = 2; // final static int SOLD = 3; public static void main(String[] args) { // TODO Auto-generated method stub //볼을 5개 가지고 있는 BallMachine을 생성 BallMachine ballMachine = new BallMachine(5); System.out.println(ballMachine.toString()); ballMachine.insertCoin(); ballMachine.turnCrank(); System.out.println(ballMachine.toString()); ballMachine.insertCoin(); ballMachine.ejectCoinI(); ballMachine.turnCrank(); System.out.println(ballMachine.toString()); } }
결과 728x90반응형'Computer Science > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 9. 옵저버 패턴 (Observer Design Pattern) (0) 2022.11.10 [디자인패턴] 8. 커맨드 패턴 (Command Design Pattern) (0) 2022.10.31 [디자인 패턴] 빌더 패턴 (Builder Design Pattern) (0) 2022.10.09 [디자인 패턴] 6. 싱글턴 패턴 (Singleton Design Pattern) (2) 2022.10.09 [디자인 패턴] 5. 스트래티지 패턴 (Strategy Design Pattern) (0) 2022.09.25