13. 상태 패턴

AlmondGood·2022년 7월 27일
0

디자인패턴

목록 보기
14/16

상태 패턴(State Pattern)

상태 패턴은 말 그대로 어떤 객체가 상태를 가지는 것을 말합니다.

어떤 상태의 객체의 A 메소드가 println() 를 실행한다면,
다른 상태일 때 A 메소드는 sc.nextLine() 을 실행합니다.

즉, 객체의 상태에 따라 객체의 행동이 달라지게 되는 것이 상태 패턴입니다.




상태 패턴 구현

버튼 하나로 켜고 끌 수 있는 전등이 있습니다.
이 전등은 켜지고 나서 10분이 지나면 자동으로 꺼지게 됩니다.
그렇다면 이 전등은 On/Off 두 가지의 상태를 가지고, 이 두 상태를 왔다갔다 해야 합니다.


먼저 인터페이스를 보겠습니다.
상태를 나타내는 인터페이스입니다.

interface LightState {
    void presentState(); // 현재 상태를 출력하는 메소드입니다.

    void pressButton(); // On/Off 버튼 메소드입니다.
}

어렵지 않네요.
늘 보던 유형의 인터페이스입니다.


전등이 켜져 있는 상태 클래스입니다.

class LightOnState implements LightState{

	// 전등 구체 객체입니다.
    Light light;

    public LightOnState(Light light) {
        this.light = light;
    }

    @Override
    public void presentState() {
        System.out.println("불이 켜져 있습니다.");
    }

    @Override
    public void pressButton() {
        System.out.println("불을 껐습니다.");

		// 불을 껐으니 불을 끈 상태로 만들어 줍니다.
        light.setLightState(light.getLightOffState());
    }
}

전등이 꺼져 있는 상태 클래스입니다.
구성은 위와 동일합니다.

class LightOffState implements LightState{
    Light light;

    public LightOffState(Light light) {
        this.light = light;
    }

    @Override
    public void presentState() {
        System.out.println("불이 꺼져 있습니다.");
    }

    @Override
    public void pressButton() {
        System.out.println("불을 켰습니다.");
        light.setLightState(light.getLightOnState());
    }
}

전등 구체 클래스입니다.
코드가 좀 길지만, 어렵지 않기 때문에 잘 따라오실 수 있습니다.

class Light {

  	// On/Off 상태 객체를 선언해 줍니다.
    private final LightState lightOnState;
    private final LightState lightOffState;

    // 이 전등의 현재 상태를 담는 참조 변수입니다.
    private LightState lightState;

    // 타이머입니다. 10이 되면 자동으로 불이 꺼집니다.
    private int cnt = 0;


	// On/Off 상태를 초기화하고, 전등 생성 시에는 꺼진 샹태입니다.
    public Light() {
        lightOnState = new LightOnState(this);
        lightOffState = new LightOffState(this);

        this.lightState = lightOffState;
    }

	// 현재 상태에 담긴 객체의 메소드를 실행시킵니다.
    public void presentLightState() {
        this.lightState.presentState();
    }
    public void pressButton() {
        this.lightState.pressButton();
    }


	// 타이머 메소드입니다.
    // 켜져있을 때만 작동하고, 꺼져있으면 현재 상태를 출력합니다.
    public void timeIsGold() {
        if (this.lightState == this.lightOnState) {
            cnt++;

            if (cnt == 10) {
                cnt = 0;

                this.lightOnState.pressButton(); // 불이 꺼지게 됩니다.
            }
        } else {
            presentLightState(); // 현재 상태(불이 꺼진 상태)를 출력하게 됩니다.
            cnt = 0;
        }
    }


	// 객체끼리 정보를 주고받는 데 사용할 메소드들입니다.
    void setLightState(LightState lightState) {
        this.lightState = lightState;
    }
    LightState getLightOnState() {
        return lightOnState;
    }
    LightState getLightOffState() {
        return lightOffState;
    }
}

꺼진 상태에서 버튼을 누르면, 켜진 상태의 객체를 가져와, 현재 상태를 켜진 상태로 만듭니다.

lightState == lightOffState
			↓
light.pressButton();
			↓
lightOffState.pressButton();
			↓
light.setLightState(light.getLightOnState());
			↓
lightState == lightOnState

반대도 똑같이 진행됩니다.




메인 함수입니다.

public static void main(String[] args) {

	Light light = new Light();

	light.presentLightState(); 	// "불이 꺼져 있습니다."
    light.pressButton();		// "불을 켰습니다."
    light.presentLightState();	// "불이 켜져 있습니다."

    light.pressButton();		// "불을 껐습니다."
    light.presentLightState();	// "불이 꺼져 있습니다."

    light.timeIsGold();			// "불이 꺼져 있습니다."
    light.pressButton();		// "불을 켰습니다."

	// 타이머입니다.
	for (int i = 0; i < 10; i++) {
    	light.timeIsGold();		// "불을 껐습니다."
    }

	light.presentLightState();	// "불이 꺼져 있습니다."
}

사용자는 light 객체 하나만을 생성하고, 버튼 하나를 조작하는 것만으로
객체가 알아서 상태를 변환합니다.

그리고 그 상태에 따라 실행되는 동작도 달라집니다.

불이 켜져있을 때불을 끄고, 꺼져있을 때불을 켭니다.




상태 패턴의 장단점

장점

  • 하나의 객체에 대한 여러 동작을 구현해야할 때 상태 객체만 수정하므로 동작의 추가, 삭제 및 수정이 간단해집니다.
  • State 패턴을 사용하면 객체의 상태에 따른 조건문(if/else, switch)이 줄어들어 코드가 간결해지고 가독성이 올라갑니다.

단점

  • 상태에 따른 조건문을 대신한 상태 객체가 증가하여 관리해야할 클래스의 수가 증가합니다.

https://always-intern.tistory.com/9




상태 패턴 vs 전략 패턴

상태 패턴과 전략 패턴의 다이어그램을 살펴보면 거의 유사한 구조임을 알 수 있습니다.

전략 패턴


https://velog.io/@y_dragonrise/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4Strategy-Pattern

상태 패턴


https://johngrib.github.io/wiki/pattern/state/

콘텍스트 객체가 각 전략, 상태 인터페이스를 변수로 가지고,
각 구체 클래스가 전략, 상태 인터페이스를 상속받습니다.

형태는 비슷하지만 두 패턴의 차이는 용도에 있습니다.

전략 패턴은 사용자가 콘텍스트 객체가 몇 가지 행동 중, 어떤 행동을 하도록 지정하는 반면
상태 패턴은 여러 상태 중, 어떤 상태를 가지면 행동 전체가 바뀌게 됩니다.

즉, 전략 패턴은 사용자가 직접 콘텍스트 객체에 어떤 행동이 있는지 알고, 직접 영향을 주어 유연성을 극대화하기 위해 사용되고,
상태 패턴은 사용자가 객체의 상태를 몰라도 알아서 상태가 변화하기 때문에, 상태 객체의 행동을 캡슐화하는 데 의미가 있습니다.

profile
Zero-Base to Solid-Base

0개의 댓글