-
ch07 스테이트패턴25 - 2/설계패턴 2025. 10. 15. 00:30
1. 개념
01. UML state diagrams
- 상태 다이어그램: 객체의 생애 동안 데이터와 행동 나타냄
- 상태 집합(초기 시작 상태 포함)
- 상태 간 전이
- 전체 다이어그램은 해당 객체의 관점에서 그려짐
- 유한 상태 기계와 유사
- 상태 다이어그램에 가장 적합한 객체
- 크고 복잡한 객체로 긴 생애 가짐
- 도메인("모델") 객체
- 시스템의 모든 클래스에 대해 상태 다이어그램을 만드는 것은 유용X
02. State
- 상태: 객체의 데이터에 대한 개념적 설명 -> 객체의 필드 값 표현
- 전체 다이어그램은 중심 객체의 관점에서 그려짐
- 이 객체가 볼 수 있고 영향 미칠 수 있는 상태/개념만 포함
- 필드의 모든 가능한 값 포함X, 개념적으로 다른 값만 포함
03. Killer rabbit의 탈옥 막기

- 초기 상태: 다이어그램의 가장 왼쪽에 있는 검은 원, 다이어그램의 시작 상태 나타냄. 여기서 시스템은 "Wait" 상태로 전환
- 대기 상태(Wait): 시스템은 대기 상태에 있으며, 여기서 특정 조건이 충족될 때 다른 상태로 전환
-> 촛불 제거되고 문이 닫힌 경우 -> 잠금(Lock) 상태로 전환 - 잠금 상태(Lock): 금고가 잠금 상태에 있으며, 특정 조건 충족 시 상태 변함
-> 열쇠 돌려지고 촛불 안에 있음 => "Open" 상태 전환
-> 열쇠 돌려지고 촛불 밖에 있음 => "킬러 토끼 방출" 트리거와 함께 최종상태로 감
04. Transitions
- 전이(transition): 한 상태에서 다른 상태로의 이동

- 시그니처 [가드] / 활동
- 시그니처: 상태 변화를 유발하는 이벤트
- 가드: 참이어야 하는 boolean 조건
- 활동: 전이 중 실행되는 행동
- 전이, 상호 배타적이어야 함
- 이벤트에 대해 어떤 전이를 선택해야 하는지가 명확해야 함
- 대부분의 전이는 즉각적, "do" 활동 제외
05. Internal activities
- 내부 활동: 중심 객체가 스스로 수행하는 행동
- 때때로 자기 전이로 그려짐
- entry/exit 활동: 해당 상태에 들어가거나 나오는 이유
06. Super/substates

- 복잡한 상태: 그 안에 하위 상태 포함할 수 O
- 더 큰 상태 안에 중첩된 둥근 사각형 - 주의: 이 기능을 과도하게 사용 X
- 하나의 상태 안에서 하위 상태와 별개의 상태를 혼동하기 쉬운 점 염두해야 함
07. 상태 다이어그램 예제


2. 상태 머신 다이어그램


3. 형광등 만들기


4. 문제점
- 형광등에 새로운 상태 추가하려면?

- 이미 ON 상태에서 한번 더 ON 누르면 SLEEPING 상태로 전환
- 상태가 추가될 수록 if문의 중첩이 복잡
5. 해결책
01. 형광등 상태
- 상태를 캡슐화

스트래티지 패턴과 비슷하지만 컨텍스트의 "상태"가 내부에 있다는 차이점 존재 - 예제 코드
- Light.java
public class Light { private State state; public Light() { state = new OFF(this); } public void setState(State state) { this.state=state; } public void on_button_pushed(){ state.on_button_pushed(this); } public void off_button_pushed(){ state.off_button_pushed(this); } }- State.java
public interface State { void on_button_pushed(Light light); void off_button_pushed(Light light); }- ON.java
public class ON implements State{ private Light light; public ON(Light light){ this.light=light; } @Override public void on_button_pushed(Light light){ System.out.println("Light SLEEPING!"); light.setState(new SLEEPING((light))); } @Override public void off_button_pushed(Light light){ System.out.println("Light off!"); light.setState(new OFF(light)); } }- OFF.java
public class OFF implements State{ private Light light; public OFF(Light light){ this.light=light; } @Override public void on_button_pushed(Light light){ System.out.println("Light On!"); light.setState(new ON(light)); } @Override public void off_button_pushed(Light light){ System.out.println("반응 없음"); } }- SLEEPING.java
public class SLEEPING implements State{ private Light light; public SLEEPING(Light light){ this.light=light; } @Override public void on_button_pushed(Light light) { System.out.println("Light On!"); light.setState(new ON(light)); } @Override public void off_button_pushed(Light light) { System.out.println("Light Off!"); light.setState(new OFF(light)); } }- Main.java
public class Main { public static void main(String[] args) { Light light=new Light(); light.off_button_pushed(); // "반응 없음" light.on_button_pushed(); // "Light ON!" light.on_button_pushed(); // "Light SLEEPING!" light.on_button_pushed(); // "Light ON!" light.off_button_pushed(); // "Light OFF!" } }- 실행 결과


02. 호텔 방 예약 시스템
- 코드
- HotelRoom.java
public class HotelRoom { private HotelRoomState state; public HotelRoom hotelRoom; public HotelRoom(){ state=new AvailableRoomState(this); // 방 생기면 어벨러블 상태 } public void setState(HotelRoomState state){ this.state=state; } public void checkIn(){ state.checkIn(); } public void checkOut(){ state.checkOut(); } public void reserveRoom(){ state.reserveRoom(); } public void cancelReservation(){ state.cancelReservation(); } public String getState(){ return state.getState(); } }- HotelRoomState.java
public interface HotelRoomState { void reserveRoom(); void checkOut(); void cancelReservation(); void checkIn(); String getState(); }- ReserveRoomState.java
public class ReserveRoomState implements HotelRoomState { private HotelRoom hotelRoom; public ReserveRoomState(HotelRoom hotelRoom){ this.hotelRoom=hotelRoom; } @Override public void reserveRoom() { System.out.println("이미 예약하셨습니다."); } @Override public void checkOut() { System.out.println("체크아웃 할 수 없습니다. 체크인부터 해주세요."); } @Override public void cancelReservation() { System.out.println("예약이 취소되었습니다."); hotelRoom.setState(new AvailableRoomState(hotelRoom)); } @Override public void checkIn() { System.out.println("체크인 완료하였습니다."); hotelRoom.setState(new OccupieRoomState(hotelRoom)); } @Override public String getState(){ return "Reserve Room"; } }- AvailableRoomState.java
public class AvailableRoomState implements HotelRoomState { private HotelRoom hotelRoom; public AvailableRoomState(HotelRoom hotelRoom){ this.hotelRoom=hotelRoom; } @Override public void reserveRoom() { System.out.println("예약이 완료되었습니다."); hotelRoom.setState(new ReserveRoomState(hotelRoom)); } @Override public void checkOut() { System.out.println("체크아웃 할 수 없습니다. 예약부터 해주세요."); } @Override public void cancelReservation() { System.out.println("예약 이력이 없으므로 취소할 수 없습니다."); } @Override public void checkIn() { System.out.println("체크인 할 수 없습니다. 예약부터 해주세요."); } @Override public String getState(){ return "Available Room"; } }- OccupieRoomState.java
public class OccupieRoomState implements HotelRoomState{ private HotelRoom hotelRoom; public OccupieRoomState(HotelRoom hotelRoom){ this.hotelRoom=hotelRoom; } @Override public void reserveRoom() { System.out.println("사용 중인 방입니다. 다른 방을 예약해주세요."); } @Override public void checkOut() { System.out.println("이용해주셔서 감사합니다. 안녕히 가십시오."); hotelRoom.setState(new AvailableRoomState(hotelRoom)); } @Override public void cancelReservation() { System.out.println("이용 중이므로 예약을 취소할 수 없습니다."); } @Override public void checkIn() { System.out.println("이미 체크인 하셨습니다."); } @Override public String getState(){ return "Occupie Room"; } }- Main.java
public class Main { public static void main(String[] args) { HotelRoom hotelRoom = new HotelRoom(); hotelRoom.reserveRoom(); // 예역되었습니다. hotelRoom.reserveRoom(); // 이미 예약됐습니다. hotelRoom.checkIn(); hotelRoom.cancelReservation(); hotelRoom.checkOut(); } }- 실행 결과

- State 장점
- 코드의 가독성 ↑: 상태별 행동을 분리
- 유연한 상태 전이: 각 상태에 대한 구체적인 클래스를 통해 유연하게 전이
- 응집도 ↑: 상태와 관련된 행동이 동일한 클래스에 모여 있어 응집도 ↑
- 확장 용이: 새로운 상태 추가 or 기존 상태 수정 시 전체 시스템에 미치는 영향 최소화할 수 O
- State 단점
- 클래스 수 ↑: 각 상태를 클래스 별 분리해야 하므로 클래스 수 많아져 복잡성 증가
- 상태 전이 관리의 복잡성: 상태 간 전이 관리하는 로직이 복잡, 실수로 잘못된 전이 발생할 수O
- 디버깅 어려움: 상태 多 디버깅 복잡, 특정 상태에서 발생하는 버그 추적 어려움
- 초기 설계의 부담: 상태와 전이에 대한 명확한 이해 필요, 초기 설계에 더 많은 노력 필요
- Strategy VS State
Strategy State 초점 패턴, 알고리즘 변경 객체 상태에 따른 행동 변화 관리 전이 방식 클라이언트가 명시적으로 알고리즘 선택 객체가 내부적으로 상태 변경하면서 적절한 행동 자동 선택 구조적 차이 알고리즘 독립적 분리 상태 간 전이, 행동을 밀접하게 연결 '25 - 2 > 설계패턴' 카테고리의 다른 글
[Week6] 6장 싱글톤패턴 실습 (0) 2025.10.15 ch06 싱글톤패턴 (0) 2025.10.15 [Week5] 5장 스트래티지 패턴 실습 (0) 2025.10.15 ch05 스트래티지 패턴 (0) 2025.10.15 [Week4] 4장 디자인패턴 개요 실습 (0) 2025.10.14