Computer Science/디자인 패턴

스테이트 패턴(State Pattern)

닉네임못짓는사람 2022. 1. 8. 03:12
반응형

스테이트 패턴


스테이트 패턴은 객체가 어떠한 동작을 수행할 때

객체의 '상태(state)'에 따라 다른 동작을 수행하도록 하는 상황에서,

객체의 상태를 직접 체크하는 것이 아닌 상태 자체를 객체화하여

상태 객체가 동작을 수행하도록 위임하는 패턴이다.

 

예시


게임 캐릭터를 가지고 예시를 한 번 들어보도록 하자.

const val SEATING = "seating"
const val STANDING = "standing"

fun main(){
    var player = Player()
    player.rightClick()
    player.setState(SEATING)
    player.rightClick()
}

class Player(){
    private var state = STANDING
    
    fun setState(state: String){
        this.state = state
    }
    
    fun rightClick(){
        if(state == STANDING){
            println("이동합니다.")
        }else{
            println("일어섭니다.")
        }
    }
}

이 캐릭터는 서있는 상태와 앉아있는 상태의 두 개의 상태를 가지고 있다.

그리고 각 상태에 따라서 우클릭을 했을 때 다른 동작을 수행하도록 되어있다.

지금 상태로는 별 다른 문제는 없어보이는데, 다른 상태를 하나 추가해보도록 하자.

const val SEATING = "seating"
const val STANDING = "standing"
const val LAYING = "laying"

.
.
.

fun rightClick(){
    if(state == STANDING){
        println("이동합니다.")
    }else if(state == SEATING){
        println("일어섭니다.")
    }else{
        println("앉습니다.")
    }
}

이번에는 캐릭터가 누워있는 상태를 하나 추가했다.

이렇게 하나의 상태가 추가되면 하나의 조건문만 추가되니까 이것도 괜찮아보인다.

 

하지만 여기에 계속해서 다른 상태가 추가되면 어떨까?

마비, 수영중, 비행중, 자는중, 죽어있는 상태 등등...

그리고 추후에 Player클래스를 상속받는 여러 캐릭터 클래스가 만들어진다면?

 

각 클래스마다 위와 같은 조건문을 짜넣는것은

코드가 길어지고, 지저분해지고, 유지보수도 힘들것이다.

 

때문에 스테이트 패턴에선 위와 같은 상황에 각 상태마다 클래스를 생성하여 캡슐화한다.

interface State{
    fun rightClick()
}

먼저, 위와 같이 상태를 캡슐화한 인터페이스를 하나 선언한다.

 

그리고 위의 인터페이스를 구현하는 각각의 상태 클래스를 정의해준다.

class Standing: State{
    override fun rightClick(){
        println("이동합니다.")
    }
}

class Seating: State{
    override fun rightClick(){
        println("일어섭니다.")
    }
}

class Laying: State{
    override fun rightClick(){
        println("앉습니다.")
    }
}

다음으로 Player클래스를 수정해주도록 하자.

class Player(){
    lateinit var playerState: State
    
    init{
        playerState = Standing()
    }
    
    fun setState(state: State){
        this.playerState = state
    }
    
    fun rightClick(){
        playerState.rightClick()
    }
}

Player클래스에서 자리를 차지하던 조건문은 사라지고,

인터페이스의 rightClick함수를 호출하기만 하면 된다.

 

이제 실제로 Player객체를 만들어서, 각 상태에서 어떻게 동작하는지 확인해보도록 하자.

fun main(){
    var player = Player()
    player.rightClick()
    player.setState(Seating())
    player.rightClick()
    player.setState(Laying())
    player.rightClick()
}

장점


  • 새로운 상태를 추가해도 코드가 받는 영향이 최소화된다.
  • 코드 내에서 길고 복잡한 조건문을 제거할 수 있다.
  • 상태에 대한 동작 수행이 각각 캡슐화 되어있기 때문에 유지보수가 편하다.

단점


  • 클래스가 너무 많아지면 오히려 관리하기 어려울 수 있다.
  • 객체의 상태의 수가 적을 경우 사용하면 불필요한 복잡성이 추가될 수 있다.
반응형