스테이트

이연희·2022년 7월 17일
0

Design_Pattern

목록 보기
4/11

스테이트 패턴

형광등 만들기

형광등의 상태를 표현하는 상수를 정의한다.

private static int ON = 0;
private static int OFF = 1;

현재 형광등의 상태를 저장하는 변수를 정의한다.

private int state;

형광등을 모델링하는 Light 클래스와 메서드로 구현한다. off_button_pushed와 on_button_pushed라는 2개의 메서드(행위)가 있으며 각각을 현재 상태에 따라 적절한 작업을 하도록 코딩한다.

public class Light{
	private static int ON = 0;
	private static int OFF = 1;
    private int state;
    
    public Light(){state=OFF;}
	
    public void on_button_pushed(){
    	if(state==ON){...}
        else{
        	...
        	state=ON;
        }
    }
    public void off_button_pushed(){
    	if(state=OFF){...}
        else{
        	...
			state=OFF;
		}
    }
}

문제점

형광등에 새로운 상태를 추가할 때, 가령 형광등에 '취침등'의 상태를 추가하려면 어떻게 해야할까?
우선 취침등 상태를 나타내는 상수인 SLEEPING를 추가한다.

private static int SLEEPING=2;

그리고 on_button_pushed 상태는 다음과 같이 변경된다.

public void on_button_pushed(){
	if(state=ON){
    	state=SLEEPING;
    }
    else if(state=SLEEPING){
    	state=ON;
    }
    else{
    	state=ON;
    }
}	

상태진입이 복잡한 조건문에 내포된 지금 구조는 현재 시스템의 상태 변화를 파악하기 어렵다. 그리고 새로운 상태가 추가되는 경우에 상태 변화를 초래하는 모든 메서드를 수정해야 한다.

해결책

무엇이 변하는가를 찾아야 한다. 변하는 부분을 찾아서 이를 캡슐화하는 것이 중요하다. 현재 시스템의 상태와 상관없게 구성하고 상태 변화에 독립적이도록 코드를 수정해야 한다.

이를 위해 상태를 클래스로 분리해 캡슐화한다. 5장에서 본 스트래티지 패턴과 구조가 동일하다. Light 클래스에서 구체적인 상태 클래스가 아닌 추상화된 State 인터페이스만 참조하므로 현재 어떤 상태에 있는지와 무관하게 코드를 작성할 수 있다. Light 클래스에서는 상태 클래스에 작업을 위임만 하면 된다.

interface State{
	public void on_button_pushed(Light light);
    public void off_button_pushed(Light light);
}

public class ON implements State{
	public void on_button_pushed(Light light){
    	System.out.println("반응 없음");
    }
    
    public void off_button_pushed(Light light){
    	System.out.println("Light off");
        light.setState(new OFF(light));
    }	
}	

public class OFF implements State{
	public void on_button_pushed(Light light){
    	System.out.println("Light ON");
        light.setState(new ON(light));
    }
    
    public void off_button_pushed(Light light){
    	System.out.println("반응 없음");
    }	
}	

아래는 스테이트 패턴으로 Light 클래스를 수정한 코드다.

public class Light{
	private State state;
    
    public Light(){
    	state = new OFF;
    }
    public void setState(State state){
    	this.state = state;
    }
    public void on_button_pushed(){
    	state.on_button_pushed(this);
    }
    public void off_button_pushed(){
    	state.off_button_pushed(this);
    }
}

Light 클래스의 state 변수를 통해 현재 시스템의 상태 객체를 참조해야한다. state 변수가 참조하는 상태 객체에 작업을 위임해야 한다. Light 클래스는 구체적인 상태를 나타내는 객체를 참조하지 않는다.
즉, Light 클래스는 시스템이 어떤 상태에 있는지와 무관하다.

그러나 위 코드는 상태 변화가 생길 때마다 새로운 객체를 생성하므로 메모리 낭비와 성능 저하를 가져올 수 있다. 그래서 싱글톤 패턴을 이용해서 코드를 개선할 수 있다.

public class ON implements State{
	private static ON on = new ON();//ON 클래스 인스턴스로 초기화
    private ON(){}
	
    public static ON getInstance(){//초기화된 ON 클래스의 인스턴스를 반환
    	return on;
    }
    
    public void on_button_pushed(Light light){
    	System.out.println("반응 없음");
    }
    
    public void off_button_pushed(Light light){
    	light.setState(OFF.getInstance());
        System.out.println("Light off"); 
    }
}

스테이트 패턴

스테이트 패턴은 어떤 행위를 수행할 때 상태에 행위를 수행하도록 위임힌다. 시스템에 각 상태를 클래스로 분리하고, 각 클래스에서 수행하는 행위들을 메서드로 구현한다. 그리고 이런 상태들을 외부로부터 캡슐화하기 위해 인터페이스를 만들어 시스템의 각 상태를 나타내는 클래스로 하여금 실체화한다.

인터페이스

State: 시스템의 모든 상태에 공통의 인터페이스를 제공한다. 이 인터페이스를 실체화한 어떤 상태 클래스도 기존 상태 클래스를 대신해 교체해서 사용할 수 있다.

구현체

State1, State2, State3: Context 객체가 요청한 작업을 자신의 방식으로 실제 실행한다. 대부분 다음 상태를 결정해 상태 변경을 Context 객체에 요청하는 역할도 수행한다.

Context 객체

Context: State를 이용하는 역할을 수행한다. 현재 시스템의 상태를 나타내는 상태 변수(state)와 실제 시스템의 상태를 구성하는 여러가지 변수가 있다. 각 상태 클래스에서 상태 변경을 요청해 상태를 발꿀 수 있도록 하는 메서드(setState)가 제공된다. Context 요소를 구현한 클래스의 request 메서드는 실제 행위를 실행하는 대신 해당 상태 객체에 행위 실행을 위임한다.

profile
공부기록

1개의 댓글

comment-user-thumbnail
2022년 7월 18일

도움이 되었습니다:)

답글 달기