February
19th,
2025
상태 패턴(State Pattern)
문제
오토마타와 유사하다
이 패턴의 주요 개념은 모든 순간에 프로그램이 속ㄷ할 수 있는 상태들의 수가 유한하다는 것이다. 어떤 상태에서든 프로그램은 다르게 행동하며, 한 상태에서 다른 상태로 즉시 전환될 수 있다. 현재의 상태에 따라 다른 상태로 전환되거나 전환 되지 않을 수 있다. 이러한 전환 규칙들을 천이(trasition)라고 한다. 이러한 규칙들은 유한하다
문서에 대해서 draft하고 moderation하고 publish하는 과정이 있다고 가정하자. 다음과 같은 코드가 나올 것이다.
class Document {
fun publish() {
if (state == "draft") {
...
} else if (state == "moderation") {
...
} else if (state == "published") {
...
}
}
}
조건문 기반의 상태머신의 단점은 상태가 추가된다면 조건문이 거대해진다. 이러한 코드는 유지관리하기가 어렵다.
해결
객체의 모든 가능한 상태들에 대해 새 클래스를 만들고 모든 상태별 행동들을 이러한 클래스들로 추출한다. context라는 객첸는 모든 행동을 자체적으로 구현하는 대신 현재 상태를 나타내는 상태 객체 중 하나에 대한 참조를 저장하고 모든 상태와 관련된 작업을 그 객체에 위임한다. context를 다른 상태로 전환하려면 활성 상태 객체를 다른 객체로 바꾸면 된다.
구조
- context는 구상 상태중 하나에 대한 참조를 저장하고 모든 상태별 작업을 위임한다. context는 상태 인터페이스를 통해 상태 객체와 통신하고 새로운 상태 전달을 위해 setter를 노출한다.
- 상태 인터페이스는 상태별 메서드를 선언한다.
- 구상상태들은 상태별 메서드들에 대한 자체적인 구현을 한다.
- context와 구상 상태 모두 context의 다음 상태를 설정할 수 있고, context에 연결된 상태 객체들을 교체하여 실제 상태를 변경할 수 있다.
예시
class Player(var state: State) {
fun changeState(state: State) {
this.state = state
}
}
abstract class State(var player: Player) {
abstract fun onLock(): String
abstract fun onPlay(): String
}
class LockedState : State {
constructor(player: Player): this(player)
override fun onLock(): String {
if (player.isPlaying()) {
player.changeState(ReadyState(player))
return "stop playing"
} else {
return "locked"
}
}
override fun onPlay(): String {
player.changeState(ReadyState(player))
return "Ready"
}
}
class ReadyState: State {
override fun onLock(): String {
player.changeState(LockedState(player))
return "Locked"
}
override fun onPlay(): String {
val action = player.startPlayback()
player.changeState(PlayingState(player))
return action
}
}
class PlayingState: State {
...
}
fun main() {
val player = Player(ReadyState())
player.onLock() // Locked
player.onPlay() // Ready
player.onPlay() // "some action"
player.onLock() // Locked
}
적용
- 현재 상태에 따라 다르게 행동하는 객체가 있을 때 상태들의 수가 많을 때 상태별 코드가 자주 변경될 때 사용
- 클래스 필드들의 현재 값들에 따라 클래스가 행동하는 방식을 변경하는 거대한 조건문들로 오염된 클래스가 있을 경우에 사용
- 상태 패턴은 유사한 상태들에 중복 코드와 조건문-기반 상태 머신의 천이가 많을 때 사용
장단점
- 단일책임원칙
- 개방/폐쇄원칙
- 거대한 상태 머신 조건문을 제거
- 상태 머신에 몇가지 상태만 있거나 머신이 거의 변경되지 않을 때 사용하는 것은 과할 수 있다