객체의 내부 상태가 변경되었을 때 객체의 행동도 변할수 있도록 하는 패턴으로 '상태'를 객체로 만들어 '상태 객체'를 변경함으로 행동을 변경하는 패턴이다.
신호등은 RED
, GREEN
, YELLOW
상태가 있다.
1. TrafficLight 클래스는 다음과 같다.
public class TrafficLight {
private String state;
public TrafficLight() {
state = "RED";
}
public void changeState() {
if ("RED".equals(state)) {
state = "GREEN";
System.out.println("RED State");
} else if ("GREEN".equals(state)) {
state = "YELLOW";
System.out.println("GREEN State");
} else if ("YELLOW".equals(state)) {
state = "RED";
System.out.println("YELLOW State");
}
}
}
위 상황에서 새로운 상태나 기존 상태가 수정될 때, 상태 전이 흐름이 바뀔 때 기존의 TrafficLight 클래스를 수정해야 한다.
객체의 상태를 또 다른 객체로 만들어 기존 객체의 상태변화와 행동을 '상태 객체'에 위임하자
1. State
interface State {
void changeState(TrafficLight trafficLight);
}
2. Red, Green, Yellow State
class RedState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("RED State");
trafficLight.setState(new GreenState());
}
}
class GreenState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("GREEN State");
trafficLight.setState(new YellowState());
}
}
class YellowState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("YELLOW State");
trafficLight.setState(new RedState());
}
}
3. TrafficLight
class TrafficLight {
private State state;
public TrafficLight() {
state = new RedState();
}
public void setState(State state) {
this.state = state;
}
public void changeState() {
state.changeState(this);
}
}
Client는 Context(TrafficLight)객체를 초기화 하는 것 외에는 상태 변환에 직접적인 개입을 하지 않는다. 패턴이 적용된 코드에서 상태추가
, 상태수정
, 상태전이변경
과 같은 작업을 수행한다면 Context가 아닌 ConcreteState에 해당하는 클래스를 변경이 필요하다.
상태변환은 다음과 같은 단계로 진행된다.
아래 코드를 보면 changeState라는 함수의 인자로 Reference를 받고 상태에 해당하는 행동(print)를 한뒤 setState로 Context의 상태를 변화시킨다.
class RedState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("RED State");
trafficLight.setState(new GreenState());
}
}
아래 코드를 보면 상태전이를 할 때 setState(new ConcreteState())
의 형태로 진행하는데 이것은 상태전이를 할 때마다 새로운 객체를 생성한다는 의미이다.
class RedState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("RED State");
trafficLight.setState(new GreenState());
}
}
class GreenState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("GREEN State");
trafficLight.setState(new YellowState());
}
}
class YellowState implements State {
public void changeState(TrafficLight trafficLight) {
System.out.println("YELLOW State");
trafficLight.setState(new RedState());
}
}
이를 해결하기 위해서 ConcreteState의 객체의 인스턴스를 하나만 유지할 필요가 있다.
객체의 인스턴스를 하나만 유지하기 위해서 싱글톤패턴을 적용한다.
아래 코드는 싱글톤패턴을 적용한 RedState의 코드이며 다른 Green, Yellow도 같은 방식으로 ConcreteState의 인스턴스를 하나만 유지할 수 있다.
class RedState implements State {
private static RedState instance = null;
private RedState() {}
public static RedState getInstance() {
if (instance == null) {
instance = new RedState();
}
return instance;
}
public void changeState(TrafficLight trafficLight) {
System.out.println("RED State");
trafficLight.setState(GreenState.getInstance());
}
}