본문 바로가기

Domain/디자인패턴

[디자인패턴] 상태 패턴(State Pattern)

상태 패턴(State Pattern)

상태 패턴은 특정 상태마다 다르게 할 일을 정의하고 나아가 상태 자체를 객체화하는 디자인 패턴이다. 가장 쉬운 예시는 TV이다. TV는 ON/OFF 버튼으로 ON 상태, OFF 상태로 변환한다.

 

 

예시

컴퓨터의 전원 버튼을 누르면 전원이 켜지고, 켜진 상태에서 다시 전원 버튼을 누르면 컴퓨터를 종료할 수 있다.

public class Computer {
    public static String ON = "on";
    public static String OFF = "off";
    private String state = "";

    public Computer(){
        setState(Laptop.OFF);
    }

    public void setState(String state){
        this.state = state;
    }

    public void pushBtn(){
        if ("on".equals(this.state)) {
            System.out.println("전원 off");
        }
        else {
            System.out.println("전원 on");
        }
    }
}

 

위 상태를 기본으로하고 절전 모드 기능이 추가되었다고 가정하자.

  • 전원이 꺼진 상태, 버튼 누를 시 절전(saving) 모드
  • 절전 모드 상태, 버튼 누를 시 전원이 켜지는 on 상태
  • 전원이 켜진 상태, 버튼 누를시 전원이 꺼지는 off 상태
public class Computer {
    public static String ON = "on";
    public static String OFF = "off";
    public static String SAVING = "saving";
    private String state = "";

    public Computer(){
        setState(Laptop.OFF);
    }

    public void setState(String state){
        this.state = state;
    }

    public void pushBtn(){
        if ("on".equals(state)) {
            System.out.println("전원 off");
        }
        else if("off".equals(state)){
            System.out.println("절전 모드");
        }
        else {
            System.out.println("전원 on");
        }
    }
}

단순히 보면 조건문만 한 줄 추가했지만 만약에 상태가 여러개가 존재할 경우 상태마다 하고자 하는 행위를 파악하기 어려워진다.

 

 

코드 개선

상태 구현

interface State{
    fun pushBtn(computer:Computer)
}

class On : State{
    companion object {
    	val on = On()
    }
    fun getInstance() = on
    
    override fun pushBtn(computer:Computer){
    	print("전원 off")
        computer.setState(Off.getInstance())
    }
    
    
}

class Off : State{
    companion object {
    	val off= Off()
    }
    
    fun getInstance() = off
    
    override fun pushBtn(computer:Computer){
    	print("절전 모드")
        computer.setState(Saving.getInstance())
    }
}

class Saving : State{
    companion object {
    	val saving = Saving()
    }
    
    fun getInstance() = saving
    
    override fun pushBtn(computer:Computer){
    	print("전원 on")
        computer.setState(On.getInstance())
    }
}

위의 companion object와 getInstance는 매번 새로운 객체를 생성하여 메모리 낭비하는 것을 방지하기 위함이다.

 

컴퓨터 구현

class Computer(private var state:State){
    init{
    	state = Off()
    }
    
    fun setState(state: State){
    	this.state=state
    }
    
    fun pushBtn(){
    	state.pushBtn(this)
    }
}

이렇게 구현하게 된다면, 클라이언트에서는 pushBtn() 메서드만 호출하면 상태에 맞게 알아서 동작하게 된다.

 

 

전략 패턴 vs 상태 패턴

  • 전략 패턴: 컨텍스트가 실행할 객체를 외부 객체(클라이언트)에서 지정해준다.
  • 상태 패턴:외부 객체(클라이언트)의 개입 없이 상태 객체 내부에서 현재 상태에 따라 컨텍스트의 상태가 변경된다.

 

요약

  • 상태패턴은 상태 별로 객체화하여 상태가 행위를 하는 패턴