의도
객체 자신의 내부 상태에 따라 행위를 변경하도록 한다. 객체가 클래스를 바꾸는 것 처럼 보일 수 있다.
Use When
- 객체의 행위가 상태에 영향을 받는 경우
- 객체의 행위가 상태에 따라 변경되며 이 상태가 런타임에 바뀌는 경우
- 복잡한 조건이 객체의 행위와 상태에 얽혀 있는 경우
- 상태 간 전이 동작이 분리되어야 하는 경우
구조도 및 구조설명
- State : 여러가지 세부 상태를 대표하는 추상 클래스
- Context : 상태에 따른 행위를 실행하는 객체
- ConcreteState : 세부 행위를 정의하는 상태객체
상태 패턴 구조도는 위 그림과 같습니다. 상태라는 개념은 하나의 클래스로 추상화 되어 있으며, 자식클래스로 세부 상태를 구현하게 됩니다. ConcreateState 에서는 각 상태에서 수행되어야 할 행위들을 구현하며, 상태의 변경 및 행위의 호출은 Context 객체 내에서 일어나게 됩니다. ( 위 구조도에서 state.handle() 에 해당 )
Context는 미리 각 세부상태를 인스턴스화 하여 객체로 만들어 두는데, 이 때 개별 세부 상태 객체들이 Context reference 를 갖게 합니다. ConcreteState가 Context 참조정보를 가져야 하는 이유는, 각 세부상태의 행위( handle() ) 수행 시 다음 상태로 변경하는 동작이 필요할 수 있기 때문입니다.
풀어서 설명하면, Context객체에서 state.handle() 실행 시 state 변수에 설정 된 ConcreteState 객체의 handle()이 호출되는데, 해당 메서드에서 필요한 동작을 수행 한 뒤, 따른 ConcreteState로 상태전이가 필요한 경우 handle()내에서 state 변경이 필요하게 됩니다. 이때, ConcreteState에 Context 객체의 참조정보가 할당되어 있다면, 아래처럼 상태변경이 가능하게 됩니다.
public void handle() {
// do handle operations
// set next state
context.state = context.anotherConcreteState;
}
샘플코드
public class StatePatternCar {
public static interface CarState {
public void speedUp(int targetSpeed);
public void speedDown(int targetSpeed);
public void enigineFaultDetected();
public void engineRepaired();
public void stop() ;
}
public static class LimpState implements CarState {
Car context;
// 생성자에서 Context 를 설정. 별도 setContext 메서드를 만들어도 무관
public LimpState (Car context){
this.context = context;
}
@Override
public void speedUp(int targetSpeed) {
// Nothing to do
}
@Override
public void speedDown(int targetSpeed) {
// Nothing to do
}
@Override
public void enigineFaultDetected() {
// Nothing to do
}
@Override
public void engineRepaired() {
context.state = new NormalState(context);
}
@Override
public void stop() {
context.speed = 0;
}
}
public static class NormalState implements CarState{
Car context;
public NormalState (Car context){
this.context = context;
}
@Override
public void speedUp(int targetSpeed) {
// TODO Auto-generated method stub
if (targetSpeed > context.speed)
context.speed = targetSpeed;
else {
if (targetSpeed > context.speed && targetSpeed < context.LIMP_MODE_MAX_SPEED)
context.speed = targetSpeed;
else
context.speed = context.LIMP_MODE_MAX_SPEED;
}
}
@Override
public void speedDown(int targetSpeed) {
if (context.speed > targetSpeed) {
context.speed = targetSpeed;
return;
}
}
@Override
public void enigineFaultDetected() {
// TODO Auto-generated method stub
context.state = new LimpState(context);
context.speed = context.LIMP_MODE_MAX_SPEED;
}
@Override
public void engineRepaired() {
// Nothing to do
}
@Override
public void stop() {
context.speed = 0;
}
}
public static class Car {
private static final int LIMP_MODE_MAX_SPEED = 100;
private int speed;
private CarState state;
public Car() {
state = new NormalState(this);
speed = 0;
}
public void speedUp(int targetSpeed) {
state.speedUp(targetSpeed);
}
public void speedDown(int targetSpeed) {
state.speedDown(targetSpeed);
}
public void enigineFaultDetected() {
state.enigineFaultDetected();
}
public void engineRepaired() {
state.engineRepaired();
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
@Override
public String toString() {
return "Car [speed=" + speed + ", state=" + ((state instanceof NormalState) ? " Normal " : " Limp ") + "]";
}
}
public static void main(String[] args) {
Car car = new Car();
car.speedUp(150);
System.out.println(car);
car.speedDown(130);
System.out.println(car);
car.enigineFaultDetected();
System.out.println(car);
car.speedUp(100);
System.out.println(car);
car.speedDown(30);
System.out.println(car);
car.speedUp(50);
System.out.println(car);
car.speedUp(100);
System.out.println(car);
car.engineRepaired();
System.out.println(car);
car.speedUp(100); System.out.println(car);
}
}
상태 패턴의 장점
상태패턴 사용의 장점은 다음과 같습니다.
- 관련 행위를 상태 안에 넣을 수 있다.
- Switch 같은 분기를 통한 상태행위를 수행하는 것이 아닌, 하나의 상태 객체에 필요한 상태 로직을 넣을 수 있다.
'CS' 카테고리의 다른 글
[디자인패턴] 중재자 패턴 (Mediator pattern) 의 개념, 구조 및 예시 요약정리 (0) | 2022.08.19 |
---|---|
[디자인패턴] 반복자 (Iterator) 패턴의 개념 및 구조 요약정리 (0) | 2022.08.18 |
[디자인패턴] GoF Pattern 요약정리와 패턴 별 활용 알아보기 (0) | 2022.08.11 |
문서 지향 데이터베이스(Document oriented database) 란 무엇인가? feat. CouchDB (0) | 2022.07.07 |
제조회사 개발자의 신세한탄 그리고 살아남기위한 발버둥 1 - 부서배치 (0) | 2022.04.24 |