JAVA로 구현하는 알고리즘과 디자인 패턴 공부: 상태 패턴 (State)
1. 상태 패턴이란? 🚦
개념: 객체의 내부 상태가 변경될 때마다 그 행동을 바꿀 수 있게 하는 패턴입니다. 상태별로 다른 행동들을 각각의 상태 클래스로 캡슐화하고, 주체 객체(Context)는 자신의 현재 상태를 나타내는 상태 객체에게 행동을 위임합니다.
자판기를 생각하면 이해하기 쉽습니다. 자판기는 자신의 상태에 따라 완전히 다르게 행동합니다.
- '동전 없음' 상태: 돈을 넣으라는 메시지만 보여줍니다.
- '동전 있음' 상태: 음료 버튼을 누를 수 있습니다.
- '음료수 품절' 상태: 돈을 넣어도 음료 버튼이 작동하지 않습니다.
이처럼, 자판기(Context)는 하나지만, 그 내부 상태(NoCoinState, HasCoinState, SoldOutState)가 무엇이냐에 따라 버튼을 눌렀을 때의 결과가 완전히 달라집니다.
2. 왜 사용할까요?
- 상태에 따른 조건문 제거: 상태에 따라 행동을 분기하는 거대한 if-else나 switch 문을 깔끔하게 제거할 수 있습니다. 각 상태에 관련된 로직은 해당 상태 클래스 안에 캡슐화됩니다.
- 개방-폐쇄 원칙 (OCP) 충족: 새로운 상태가 추가되더라도, 기존의 상태 클래스나 컨텍스트 클래스를 수정할 필요 없이 새로운 상태 클래스만 추가하면 됩니다.
- 상태 전환 로직의 명확화: 각 상태 클래스가 다음 상태로 전환되는 책임을 직접 가질 수 있어, 상태 변경 로직을 한 곳에서 명확하게 관리할 수 있습니다.
3. Java로 구현하는 방법
시나리오: 온라인 문서의 게시 워크플로우를 만들어 보겠습니다. 문서는 '초고(Draft)', '검토 중(Moderation)', '게시됨(Published)' 상태를 가지며, 각 상태에서 '게시(publish)' 버튼을 눌렀을 때의 행동이 다릅니다.
1단계: 상태(State) 인터페이스 정의
모든 상태 클래스가 구현해야 할 공통 규약입니다. 각 상태에서 가능한 행동들을 메서드로 정의합니다.
// 모든 상태가 구현할 공통 인터페이스
public interface State {
void publish(Document document);
}
2단계: 구체적인 상태(Concrete State) 클래스 구현
각각의 상태에 해당하는 행동과 상태 전환 로직을 구현합니다.
// 상태 1: 초고 상태
public class DraftState implements State {
@Override
public void publish(Document document) {
System.out.println("'초고' 상태의 문서를 '검토 중' 상태로 변경합니다.");
// 상태를 '검토 중'으로 전환
document.changeState(new ModerationState());
}
}
// 상태 2: 검토 중 상태
public class ModerationState implements State {
@Override
public void publish(Document document) {
System.out.println("'검토 중' 상태의 문서를 '게시됨' 상태로 변경합니다.");
// 상태를 '게시됨'으로 전환
document.changeState(new PublishedState());
}
}
// 상태 3: 게시됨 상태
public class PublishedState implements State {
@Override
public void publish(Document document) {
System.out.println("이미 '게시됨' 상태이므로 아무 작업도 하지 않습니다.");
// 상태 변경 없음
}
}
3단계: 컨텍스트(Context) 클래스 구현
상태를 가지는 주체 객체입니다. 현재 상태를 나타내는 멤버 변수를 가지며, 행동 요청이 오면 현재 상태 객체에 그 행동을 위임합니다.
public class Document {
// 현재 상태를 저장하는 변수
private State state;
public Document() {
// 문서는 초기에 '초고' 상태로 시작
this.state = new DraftState();
}
// 상태를 변경하는 메서드
public void changeState(State state) {
this.state = state;
}
// 행동 요청이 오면 현재 상태 객체에 위임한다.
public void publish() {
state.publish(this);
}
}
4단계: 클라이언트 코드에서 사용하기
클라이언트는 컨텍스트 객체를 생성하고, 행동을 요청하며 상태가 변하는 것을 확인합니다.
public class Main {
public static void main(String[] args) {
// 1. 새로운 문서 생성 (초기 상태: Draft)
Document document = new Document();
// 2. 첫 번째 게시 요청
// 현재 상태가 DraftState이므로, ModerationState로 상태가 전환된다.
document.publish(); // '초고' -> '검토 중'
// 3. 두 번째 게시 요청
// 현재 상태가 ModerationState이므로, PublishedState로 상태가 전환된다.
document.publish(); // '검토 중' -> '게시됨'
// 4. 세 번째 게시 요청
// 현재 상태가 PublishedState이므로, 아무 일도 일어나지 않는다.
document.publish(); // 변화 없음
}
}
4. 상태 패턴과 실제 사례
- TCP/IP 프로토콜: TCP 연결은 LISTEN, SYN-SENT, ESTABLISHED, FIN-WAIT, CLOSED 등 다양한 상태를 가집니다. 각 상태에서 데이터를 받거나 특정 신호를 받았을 때의 행동과 다음 상태가 명확하게 정의되어 있어, 상태 패턴으로 모델링하기에 완벽합니다.
- UI 컴포넌트: 마우스 커서가 올라갔을 때(Hover), 클릭했을 때(Pressed), 비활성화되었을 때(Disabled) 등 UI 컴포넌트의 상태에 따라 모양이나 행동이 바뀌는 것을 상태 패턴으로 구현할 수 있습니다.
- 게임 캐릭터: 캐릭터가 '정상', '독', '마비', '기절' 등 다양한 상태 이상에 걸렸을 때, 각 상태에 따라 행동이 제약되거나 추가적인 데미지를 입는 로직을 상태 패턴으로 깔끔하게 관리할 수 있습니다.
5. 문제 제시 및 답변 💡
문제: 신호등 시스템을 만드려고 합니다. 신호등은 '빨간불', '노란불', '초록불'의 3가지 상태를 가집니다. 시간이 지나면 change() 메서드가 호출되고, 신호등은 다음 상태로 바뀌어야 합니다 ('초록불' → '노란불' → '빨간불' → '초록불' ...).
상태 패턴을 사용하여 이 신호등 시스템을 어떻게 설계할 수 있을까요?
답변:
문서 게시 예제와 거의 동일한 구조로 설계할 수 있습니다.
- TrafficLightState 인터페이스: 모든 신호등 상태가 구현할 공통 인터페이스를 만듭니다. change(TrafficLight light) 메서드를 가집니다.
- 구체적인 상태 클래스:
- GreenLightState: change() 메서드가 호출되면, 신호등의 상태를 YellowLightState로 변경합니다.
- YellowLightState: change() 메서드가 호출되면, 신호등의 상태를 RedLightState로 변경합니다.
- RedLightState: change() 메서드가 호출되면, 신호등의 상태를 GreenLightState로 변경합니다.
- TrafficLight 클래스 (Context): 현재 상태를 저장하는 TrafficLightState 타입의 멤버 변수를 가집니다. change() 메서드가 호출되면, 현재 상태 객체의 change() 메서드에 자기 자신을 넘겨주어 상태 전환을 위임합니다.
이렇게 설계하면, TrafficLight 클래스는 복잡한 if (currentState == RED) { ... } else if (currentState == GREEN) { ... } 같은 조건문 없이, 오직 현재 상태 객체에게 모든 행동과 상태 전환의 책임을 맡기므로 매우 깔끔한 코드를 유지할 수 있습니다.
'프로그래밍 > 알고리즘&자료구조&패턴' 카테고리의 다른 글
| 디자인 패턴 - 그외 (2) | 2025.08.18 |
|---|---|
| 디자인패턴 - 템플릿 매서드 패턴 (2) | 2025.08.18 |
| 디자인패턴 - 커맨드 패턴 (1) | 2025.08.18 |
| 디자인패턴 - 옵저버 패턴 (2) | 2025.08.18 |
| 디자인패턴 - 전략패턴 (3) | 2025.08.18 |