상태(State) 패턴
**상태 패턴(State Pattern)**은 객체의 내부 상태에 따라 행동이 바뀌도록 설계하는 행동 디자인 패턴입니다. 이 패턴에서는 상태 객체를 별도로 정의하고, 객체가 상태를 변경할 때 해당 상태에 맞는 행동을 위임합니다. 조건문을 통한 상태 변경 코드의 복잡성을 줄이고, 유연한 상태 전환을 구현할 수 있습니다.
문제점: 상태에 따라 다른 동작을 수행해야 할 때의 코드 복잡성
어떤 객체가 여러 상태를 가지며, 각 상태에 따라 다른 행동을 수행해야 할 때, 상태를 if-else
나 switch
로 처리하면 코드가 복잡해집니다.
또한 새로운 상태를 추가하거나 수정할 때 코드 변경이 필요합니다.
class TrafficLight {
private String state = "RED"; // 현재 상태
public void change() {
if (state.equals("RED")) {
System.out.println("Stop!");
state = "GREEN";
} else if (state.equals("GREEN")) {
System.out.println("Go!");
state = "YELLOW";
} else if (state.equals("YELLOW")) {
System.out.println("Caution!");
state = "RED";
}
}
}
public class Main {
public static void main(String[] args) {
TrafficLight trafficLight = new TrafficLight();
trafficLight.change(); // Stop!
trafficLight.change(); // Go!
trafficLight.change(); // Caution!
}
}
문제점
- 조건문이 많아질수록 코드 복잡도 증가: 새로운 상태를 추가하려면 여러 조건문을 수정해야 합니다.
- 확장성 문제: 상태별 동작이 명확하게 분리되지 않아 유지보수가 어렵습니다.
- 객체의 책임 증가: 하나의 객체가 모든 상태와 동작을 관리합니다.
해결 방법: 상태 패턴 적용
상태 패턴은 각 상태를 별도의 클래스로 구현하고, 객체가 상태를 변경할 때 해당 상태 객체에 행동을 위임합니다.
이로써 상태별 로직이 분리되고, 상태 추가와 확장이 쉬워집니다.
상태 인터페이스 정의
interface State {
void handle(TrafficLight trafficLight);
}
구체적인 상태 클래스 구현
class RedState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Stop!");
trafficLight.setState(new GreenState());
}
}
class GreenState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Go!");
trafficLight.setState(new YellowState());
}
}
class YellowState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Caution!");
trafficLight.setState(new RedState());
}
}
Context 클래스 구현
class TrafficLight {
private State state;
public TrafficLight() {
state = new RedState(); // 초기 상태 설정
}
public void setState(State state) {
this.state = state;
}
public void change() {
state.handle(this);
}
}
클라이언트 코드
public class Main {
public static void main(String[] args) {
TrafficLight trafficLight = new TrafficLight();
trafficLight.change(); // Stop!
trafficLight.change(); // Go!
trafficLight.change(); // Caution!
trafficLight.change(); // Stop!
}
}
상태 패턴의 동작 원리
- Context 객체는 상태를 유지하며, 상태 객체에 행동을 위임합니다.
- 각 State 객체는 해당 상태에 맞는 동작을 구현하며, 필요할 때 다음 상태로 전환합니다.
- 상태 변경은 객체 내부에서 자동으로 이루어지며, 클라이언트는 상태 객체의 내부 구현을 알 필요가 없습니다.
상태 패턴의 장단점
장점
- 코드 복잡도 감소: 상태별 행동을 별도의 클래스로 분리하여 조건문을 제거합니다.
- 확장성 증가: 새로운 상태를 쉽게 추가할 수 있으며, 기존 코드를 수정할 필요가 없습니다.
- 유지보수 용이: 상태별 로직이 분리되어 코드 가독성이 향상됩니다.
- 객체의 책임 분리: 상태별로 객체의 행동을 정의하여 단일 책임 원칙(SRP)을 준수합니다.
단점
- 클래스 수 증가: 각 상태마다 클래스를 만들어야 하므로 클래스 수가 많아질 수 있습니다.
- 간단한 경우 오히려 복잡도 증가: 상태가 많지 않은 경우에는 불필요하게 복잡해질 수 있습니다.
결론
상태 패턴은 객체의 행동이 상태에 따라 달라지는 상황에서 사용하기 적합한 패턴입니다. 이 패턴을 사용하면 조건문을 제거하고 상태별 로직을 명확하게 분리할 수 있습니다.
다만, 상태가 많지 않은 간단한 경우에는 불필요한 복잡도를 추가할 수 있으므로, 상태 변화가 복잡한 시스템에 적합합니다. GUI 컴포넌트, 게임 개발, 트랜잭션 관리와 같은 영역에서 자주 사용됩니다.