[디자인 패턴] 상태 패턴 (State Pattern)
1. 상태 패턴 (State Pattern) 이란?
상태에 따라 다른 행위를 할 때 상태를 객체화 하여 각각 다른 행동을 처리할 수 있게 하는 패턴
2. 특징
기능을 캡슐화 한 Interface와 각 상태 또는 상황 별 Class 로 이루어져 있고
상태 별로 기능을 구현하여 사용한다.
3. 사용 예시
문 열기, 문 닫기, 문 잠그기, 문 잠금해제하기 4가지의 기능이 있는 기계가 있을 때
현재 문의 상태에 따라서 기능이 달라 질 수 있다.
기능
- open(): 문을 여는 행동
- close(): 문을 닫는 행동
- lock(): 문을 잠그는 행동
- unlock(): 문을 잠금 해제하는 행동
기능을 정의한 DoorState Interface 를 생성한다.
interface DoorState {
void open(Door door);
void close(Door door);
void lock(Door door);
void unlock(Door door);
}
문의 상태를 관리하고 행동을 실행할 Door Class 를 생성한다.
class Door {
private DoorState state;
public Door() {
this.state = new ClosedState(); // 초기 상태는 닫힌 상태
}
public void setState(DoorState state) {
this.state = state;
}
public void open() {
state.open(this);
}
public void close() {
state.close(this);
}
public void lock() {
state.lock(this);
}
public void unlock() {
state.unlock(this);
}
}
상태
- ClosedState: 문이 닫혀있는 상태
- OpenedState: 문이 열린 상태
- LockedState: 문이 잠긴 상태
문이 닫힌 상태 Class 를 생성하고 닫힌 상태에서 기능 별 동작을 구현한다.
open() -> 현재 상태 문 열림으로 변경
close() -> 상태 변화X (이미 닫혀 있음)
lock() -> 현재 상태 문 잠김으로 변경
unlock() -> 상태 변화X (잠겨 있지 않음)
class ClosedState implements DoorState {
@Override
public void open(Door door) {
System.out.println("문을 엽니다.");
door.setState(new OpenedState());
}
@Override
public void close(Door door) {
System.out.println("문은 이미 닫혀 있습니다.");
}
@Override
public void lock(Door door) {
System.out.println("문을 잠급니다.");
door.setState(new LockedState());
}
@Override
public void unlock(Door door) {
System.out.println("문은 잠겨 있지 않습니다.");
}
}
문이 열린 상태 Class 를 생성하고 닫힌 상태에서 기능 별 동작을 구현한다.
open() -> 상태 변화X (이미 열려 있음)
close() -> 현재 상태 문 닫힘으로 변경
lock() -> 상태 변화X (문 열려 있어 잠글 수 없음)
unlock() -> 상태 변화X (잠겨 있지 않음)
class OpenedState implements DoorState {
@Override
public void open(Door door) {
System.out.println("문은 이미 열려 있습니다.");
}
@Override
public void close(Door door) {
System.out.println("문을 닫습니다.");
door.setState(new ClosedState());
}
@Override
public void lock(Door door) {
System.out.println("문은 열려 있어 잠글 수 없습니다.");
}
@Override
public void unlock(Door door) {
System.out.println("문은 잠겨 있지 않습니다.");
}
}
문이 잠긴 상태 Class 를 생성하고 닫힌 상태에서 기능 별 동작을 구현한다.
open() -> 상태 변화X (잠겨 있어 열 수 없음)
close() -> 상태 변화X (이미 닫혀 있음)
lock() -> 상태 변화X (이미 잠겨 있음)
unlock() -> 현재 상태 문 닫힘으로 변경
class LockedState implements DoorState {
@Override
public void open(Door door) {
System.out.println("문이 잠겨 있어 열 수 없습니다.");
}
@Override
public void close(Door door) {
System.out.println("문은 이미 닫혀 있습니다.");
}
@Override
public void lock(Door door) {
System.out.println("문은 이미 잠겨 있습니다.");
}
@Override
public void unlock(Door door) {
System.out.println("문을 잠금 해제합니다.");
door.setState(new ClosedState());
}
}
사용 예시
public class Main {
public static void main(String[] args) {
Door door = new Door();
// 문 열기
door.open(); // console: 문을 엽니다.
// 문 닫기
door.close(); // console: 문을 닫습니다.
// 문 잠그기
door.lock(); // console: 문을 잠급니다.
// 문 열기 시도 (잠금 상태)
door.open(); // console: 문이 잠겨 있어 열 수 없습니다.
// 문 잠금 해제
door.unlock(); // console: 문을 잠금 해제합니다.
// 다시 문 열기
door.open(); // console: 문을 엽니다.
}
}
4. 장단점
장점
- 상태 별 행동을 명확히 분리할 수 있음
- 상태가 변경될 때마다 자동으로 상태에 맞는 동작이 실행되므로 일관성 있음
단점
- 상태가 많아지면 상태 클래스가 많아져 코드가 과도하게 복잡해 질 수 있음
5. 결론
상태 패턴은 상태 전환이 빈번하고 각 상태에 따른 행동이 달라야 하는 시스템에서 유용하게 사용 할 수 있음.
상태별로 동작을 캡슐화하고 관리할 수 있어 유지보수가 편리해지고 확장성이 높다는 장점이 있으나
상태 전환이 단순하거나 상태가 적은 경우에는 오히려 이 패턴을 적용하는 것이 과도할 수 있음