State Pattern

2018.08.07

Park Young Jun

형광등 요구사항

  1. 형광등이 꺼져 있을 때 ON 버튼을 누르면 켜진다.
  2. 형광등이 켜져 있을 때 OFF 버튼을 누르면 꺼진다.
  3. 형광등이 켜져 있을 때 ON 버튼을 누르면 아무 변화가 없어야 한다.
  4. 형광등이 꺼져 있을 때 OFF 버튼을 누르면 아무 변화가 없어야 한다.
  5. 형광등은 처음에 꺼져 있는 상태로 가정한다.

단순한 형광등


class Light {
	private static int ON = 0;
	private static int OFF = 1;
	private int state;

	public Light() {
		this.state = OFF;
	}

	public void onButtonPushed() {
		if (state == ON) {
			System.out.println("no action");
		} else {
			System.out.println("Light On");
			state = ON;
		}
	}

	public void offButtonPushed() {
		if (state == OFF) {
			System.out.println("no action");
		} else {
			System.out.println("Light Off");
			state = OFF;
		}
	}
}

형광등 요구사항 변경

  1. 형광등이 꺼져 있을 때 ON 버튼을 누르면 켜진다.
  2. 형광등이 켜져 있을 때 OFF 버튼을 누르면 꺼진다.
  3. 형광등이 켜져 있을 때 ON 버튼을 누르면 취침등이 켜진다.
  4. 형광등이 취침등 상태일 때 ON 버튼을 누르면 형광등이 켜진다. 
  5. 형광등이 꺼져 있을 때 OFF 버튼을 누르면 아무 변화가 없어야 한다.
  6. 형광등이 취침등 상태일 때 OFF 버튼을 누르면 형광등이 꺼진다.
  7. 형광등은 처음에 꺼져 있는 상태로 가정한다.

수정된 형광등


class Light {
	private static int ON = 0;
	private static int OFF = 1;
	private static int SLEEPING = 2; // 취침등 상태 추가

	private int state;

	public Light() {
		this.state = OFF;
	}

	public void onButtonPushed() {
		if (state == ON) {
			System.out.println("취침등 상태");
			state = SLEEPING;
		} else if (state == SLEEPING) { // 취침등 조건 추가
			System.out.println("Light On");
			state = ON;
		} else {
			System.out.println("Light On");
			state = ON;
		}
	}

	public void offButtonPushed() {
		if (state == OFF) {
			System.out.println("no action");
		} else if (state == SLEEPING) { // 취침등 조건 추가
			System.out.println("Light Off");
			state = OFF;
		} else {
			System.out.println("Light Off");
			state = OFF;
		}
	}
}

형광등의 문제점


class Light {
	private static int ON = 0;
	private static int OFF = 1;
	private static int SLEEPING = 2; // 취침등 상태 추가

	private int state;

	public Light() {
		this.state = OFF;
	}

	public void onButtonPushed() {
		if (state == ON) {
			System.out.println("취침등 상태");
			state = SLEEPING;
		} else if (state == SLEEPING) { // 취침등 조건 추가
			System.out.println("Light On");
			state = ON;
		} else {
			System.out.println("Light On");
			state = ON;
		}
	}

	public void offButtonPushed() {
		if (state == OFF) {
			System.out.println("no action");
		} else if (state == SLEEPING) { // 취침등 조건 추가
			System.out.println("Light Off");
			state = OFF;
		} else {
			System.out.println("Light Off");
			state = OFF;
		}
	}
}
  • 복잡한 조건문에 상태가 숨어있어 상태 변화를 파악하기 힘듦
  • 새로운 상태가 추가되는 경우 모든 메서드를 수정해야 한다.

문제점 해결하기

  • 변하는 부분을 찾아서 캡슐화 한다

  • 시스템이 어떤 상태에 있는지와 상관없게 구성한다

  • 상태 변화에 독립적이도록 코드를 수정한다

문제점 해결하기

  • 변하는 것은 상태. 상태를 클래스로 분리해 캡슐화 하자

  • 상태에 의존적인 행위, 즉 상태에 따라 달라지는 행위는 상태 클래스에 두어 특정 상태에 따른 행위를 구현하도록 하자

스테이트 패턴을 적용한 형광등 다이어그램

스테이트 패턴을 적용한 형광등 다이어그램

  • 전략 패턴과 구조가 동일함

 

  • Light 클래스는 State 인터페이스만 참조하므로 현재 상태와 무관한 코드 작성 가능

 

  • Light 클래스는 State 클래스에 작업을 위임만 하면 된다

 

 

스테이트 패턴을 적용한 형광등 코드

State 인터페이스 정의


public interface State {
	void onButtonPushed(Light light);
	void offButtonPushed(Light light);
}

스테이트 패턴을 적용한 형광등 코드

각 상태별 구현체 구현

public class OFF implements State {
    private static OFF off = new OFF();
    private OFF () {}

    public static OFF getInstance() {
    	return off;
    }   	

    @Override    
    public void onButtonPushed(Light light) {
    	System.out.println("Light On");
    	light.setState(ON.getInstance());
    }

    @Override
    public void offButtonPushed(Light light) {
    	System.out.println("noAction");
    }
}
public class ON implements State {
    private static ON on = new ON();
    private ON() {}

    public static ON getInstance() {
    	return on;
    }

    @Override
    public void onButtonPushed(Light light) {
        System.out.println("취침등 상태");
	light.setState(SLEEPING.getInstance());
    }

    @Override
    public void offButtonPushed(Light light) {
	System.out.println("Light Off");
	light.setState(OFF.getInstance());
    }
}
public class SLEEPING implements State {
    private static SLEEPING sleeping = new SLEEPING();    
    private SLEEPING() {}

    public static SLEEPING getInstance() {
	return sleeping;
    }

    @Override
    public void onButtonPushed(Light light) {
	System.out.println("Light On");
	light.setState(ON.getInstance());
    }

    @Override
    public void offButtonPushed(Light light) {
	System.out.println("Light Off");
	light.setState(OFF.getInstance());
    }
}

스테이트 패턴을 적용한 형광등 코드

수정된 Light 클래스

public class Light {
	private State state;

	public Light() {
		this.state = OFF.getInstance();
	}

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

	public void onButtonPushed() {
		state.onButtonPushed(this);
	}

	public void offButtonPushed() {
		state.offButtonPushed(this);
	}
}

Client 클래스

public class Client {
    public static void main(String[] args) {
        Light light = new Light();
        
        light.onButtonPushed();
        light.onButtonPushed();
        light.offButtonPushed();
        light.offButtonPushed();
    }
}
Light On
취침등 상태
Light Off
noAction

실행결과

스테이트 패턴을 적용한 형광등 코드

State를 Enum으로 변경

public enum State {
    ON {
	public void onButtonPushed(Light light) {//...}
	public void offButtonPushed(Light light) {//...}
    },
    OFF {
	public void onButtonPushed(Light light) {//...}
	public void offButtonPushed(Light light) {//...}
    },
    SLEEPING {
	public void onButtonPushed(Light light) {//...}
	public void offButtonPushed(Light light) {//...}
    };

    public abstract void onButtonPushed(Light light);
    public abstract void offButtonPushed(Light light);
}

State Pattern (상태 패턴)

  • 상태에 따라 동일한 작업이 다른 방식으로 실행되는 경우, 상태가 작업을 수행하도록 위임하는 디자인 패턴
  • 시스템의 각 상태를 클래스로 분리
  • 각 클래스에서 수행하는 행위를 메서드로 구현
  • 상태는 인터페이스로 만들어서 캡슐화
  • 상태 스스로 다음 상태를 결정

State Pattern

By Young Jun Park (박영준)

State Pattern

State Pattern(상태 패턴)에 대해 소개합니다.

  • 210