본문 바로가기

디자인패턴

[디자인패턴] 스테이트 패턴 (State Pattern)

스테이트 패턴 (State Pattern) 이란

현재의 다양한  상태에 따라 동일한 입력이 들어와도 다른 결과를 낸다. 이때, 현재 시스템이 상태 변화에 독립적이도록 "상태"를 캡슐화하며 구체적 "상태"에서 행위를 수행하도록 위임하는 패턴이다.

GoF 디자인 패턴 분류에 따르면, 클래스/객체 사이의 책임을 분배하여 결합도를 최소화하는 행위 패턴 (Behavior Pattern) 이다.

 

UML 은 아래와 같이 구성이 된다.

 

state pattern

 

 

Context 클래스에서 자신의 현재 상태에 따라 로직을 구현하게 될 경우,

복잡한 조건문과 분기문들로 이해하기 어려운 코드가 작성될 것이다. 따라서, 새로운 상태가 추가될 때 알맞은 위치에 알맞은 코드를 작성하기 어려울 것이다.

 


모니터의 전원 버튼을 켜고/끄는 상태를 State Pattern 으로 만들어 보면서 스테이트 패턴을 이해해보려고 한다.

 

Context 에 해당하는 Monitor 클래스 이다.  상태에 따른 수행이 복잡한 조건문들로 구현된 것이 아니라

State 클래스와 aggregation 관계를 맺으며, State 클래스로 행위 수행을 온전히 위임하고 있는 것을 확인할 수 있다.

class Monitor { // Context
    private var state: State

    init {
        state = OFF
    }

    fun setState(state: State) {
        this.state = state
    }

    fun onBtnClicked() {
        state.onBtnClicked(this)
    }

    fun offBtnClicked() {
        state.offBtnClicked(this)
    }
}

 

State 인터페이스와 이를 구현한 구체적 상태 ON과 OFF 클래스이다.

구체적 상태에서 입력이 들어왔을 때 어떻게 동작을 할지를 구현하였음을 확인할 수 있다.

interface State {
    fun onBtnClicked(monitor: Monitor)
    fun offBtnClicked(monitor: Monitor)
}
object ON : State {
    override fun onBtnClicked(monitor: Monitor) {
        println("모니터 유지")
    }

    override fun offBtnClicked(monitor: Monitor) {
        monitor.setState(OFF)
        println("모니터 OFF")
    }
}
object OFF : State {
    override fun onBtnClicked(monitor: Monitor) {
        monitor.setState(ON)
        println("모니터 ON")
    }

    override fun offBtnClicked(monitor: Monitor) {
        println("모니터 유지")
    }
}

 

아래와 같이 Client 에서 사용할 수 있으며, 실행결과는 아래와 같다.

@Test
fun testStatePattern() {
    val monitor = Monitor()
    monitor?.apply { 
        onBtnClicked()
        offBtnClicked()
        offBtnClicked()
        onBtnClicked()
    }
}

실행 결과

모니터 ON
모니터 OFF
모니터 유지
모니터 ON

 


이 상태에서 새로운 상태인 "잠자기" 상태를 추가한다면, Monitor 클래스 (Context) 의 코드는 변경되지 않으며

SLEEP 상태 클래스를 추가하고 각 구체적 상태 클래스마다 로직을 수정해주면 된다.

 

  • ON 상태에서 on 버튼 클릭시 SLEEP 상태 / SLEEP 상태에서 on 버튼 클릭시 ON 상태 / SLEEP 상태에서 off 버튼 클릭시 off 상태로 변경되며, 이외에는 동일하다고 가정해보자.
object SLEEP : State {
    override fun onBtnClicked(monitor: Monitor) {
        monitor.setState(ON)
        println("모니터 ON")
    }
    
    override fun offBtnClicked(monitor: Monitor) {
        monitor.setState(OFF)
        println("모니터 OFF")
    }
}
object ON : State {
    override fun onBtnClicked(monitor: Monitor) { // 변경.
        monitor.setState(SLEEP)
        println("모니터 SLEEP")
    }

    override fun offBtnClicked(monitor: Monitor) {
        monitor.setState(OFF)
        println("모니터 OFF")
    }
}

 

 

아래와 같이 Client 에서 사용할 수 있으며, 실행결과를 통해 원하는 대로 구현되었음을 확인할 수 있다.

@Test
fun testStatePattern() {
    val monitor = Monitor()
    monitor?.apply {
        onBtnClicked() // off -> on
        onBtnClicked() // on -> sleep
        onBtnClicked() // sleep -> on
            
        offBtnClicked() // on -> off
        offBtnClicked() // off 유지
        onBtnClicked()  // off -> on
    }
}

실행결과

모니터 ON
모니터 SLEEP
모니터 ON
모니터 OFF
모니터 유지
모니터 ON

'디자인패턴' 카테고리의 다른 글

[디자인패턴] 데코레이터 패턴 (Decorator Pattern)  (0) 2020.03.15
SOLID 원칙  (0) 2019.05.06