모종닷컴

유한 상태 기계 (Finite-State Machine, FSM) 본문

기술 용어

유한 상태 기계 (Finite-State Machine, FSM)

모종 2022. 8. 16. 23:57
반응형

유한 상태 기계

컴퓨터 프로그램을 설계할 때 쓰이는 모델이라고 설명되어 있습니다. 간단하게 '상태 기계'라고 부르기도 한다고 하네요. 저는 주로 상태 기계라는 용어로 많이 들었던 것 같아요. 이름으로 짐작할 수 있는데 유한 상태 기계는 유한한 개수의 상태를 가질 수 있음을 말합니다. 또한 몇 가지 조건이 붙는데 이 유한 상태 기계는 오로지 하나의 상태만 가질 수 있으며 특정 Event에 의해서만 다른 상태로 변화할 수 있다고 합니다. 그리고 이렇게 Event에 의해 상태가 변화하는 것을 전이(Transition)이라고 합니다. 

개발자의 전이

일상생활에서 유한 상태 기계를 찾아보자.

가장 대표적으로 언급되는 일중 하나는 스위치입니다. 스위치는 ON, OFF 두 개의 상태만 가질 수 있습니다. 그러면 생각해볼게요. 각각의 상태로 전이할 수 있는 동작이 무엇이 있을까요? OFF인 스위치를 ON 상태로 만들기 위해서는 '스위치를 위로 올린다'라고 볼 수 있을 것 같고, ON 상태인 스위치를 OFF로 만들기 위해서는 '스위치를 아래로 내린다'로 볼 수 있겠네요.

저는 커피를 굉장히 좋아하는데 커피 머신을 코드로 표현해보도록 하겠습니다. 대충 생각하면서 짜놓은 거라 말도 안 되는 기계가 만들어질 수 있지만 그냥 넘어가 주세요 ㅎㅎ..

상태 클래스

abstract class CoffeeMachineStatus {
    abstract val desc: String // 커피 머신에 표시될 상태 이름.

    /**
     * [toBe] 상태로 전이한다.
     * @param toBe 변경하려는 상태
     * @return 변경된 상태
     * @throws IllegalArgumentException
     * */
    fun transitionTo(toBe: CoffeeMachineStatus): CoffeeMachineStatus {
        return if (toBe is Initialize) {
            Ready()
        } else {
            require(isChangeable(toBe))
            toBe
        }
    }

    /**
     * 현재 상태가 [toBe]로 전이할 수 있는지를 판단.
     * */
    @kotlin.jvm.Throws(IllegalArgumentException::class)
    abstract fun isChangeable(toBe: CoffeeMachineStatus): Boolean
}

class Ready : CoffeeMachineStatus() {
    override val desc = "준비 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Order
}

class Order : CoffeeMachineStatus() {
    override val desc = "주문 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Calc
}

class Calc : CoffeeMachineStatus() {
    override val desc = "계산 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Making
}


class Making : CoffeeMachineStatus() {
    override val desc = "커피 내리는 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Complete
}

class Complete : CoffeeMachineStatus() {
    override val desc = "완료 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Ready
}

class Initialize : CoffeeMachineStatus() {
    override val desc = "초기화 상태"
    override fun isChangeable(toBe: CoffeeMachineStatus) = toBe is Ready
}
  • 각 상태에서는 전이할 수 있는 상태를 정의합니다. 

커피 머신

class CoffeeMachine {
    private var status: CoffeeMachineStatus // 하나의 상태만 가진다. 최초 상태는 READY

    fun printStatus() {
        log.info { "현재 기계 상태 : ${status.desc}" }
    }

    init {
        this.status = Ready()
    }

    fun calc() {
        log.info { "계산 시작." }
        changeStatus(Calc())
    }

    fun order() {
        log.info { "주문 시작." }
        changeStatus(Order())
    }

    fun making() {
        log.info { "커피 만들기 시작." }
        changeStatus(Making())
    }

    fun complete() {
        log.info { "커피가 만들어졌습니다." }
        changeStatus(Complete())
    }

    fun initialize() {
        log.info { "기계를 초기화합니다." }
        changeStatus(Initialize())
    }

    private fun changeStatus(toBe: CoffeeMachineStatus) {
        try {
            this.status = status.transitionTo(toBe)
        } catch (e: IllegalArgumentException) {
            log.info { "기계가 오동작 하였습니다. 초기화합니다." }
            this.status = status.transitionTo(Initialize())
        }
    }
}
  • 하나의 상태만 가집니다.
  • 최초 생성시 대기 상태로 초기화합니다.
  • 특정 메서드(=이벤트)가 일어나면 상태 전이가 일어납니다.
  • 상태 전이에 실패하면 초기화 상태로 돌립니다.

클라이언트

class Client {
    private lateinit var coffeeMachine: CoffeeMachine

    @Before
    fun init() {
        coffeeMachine = CoffeeMachine()
    }

    @Test
    fun `정상적인 주문`() {
        coffeeMachine.order()
        coffeeMachine.printStatus()

        coffeeMachine.calc()
        coffeeMachine.printStatus()

        coffeeMachine.making()
        coffeeMachine.printStatus()

        coffeeMachine.complete()
        coffeeMachine.printStatus()

        coffeeMachine.initialize()
        coffeeMachine.printStatus()
    }

    @Test
    fun `손님 뭘 드시고 싶으신거에요`() {
        coffeeMachine.calc()
        coffeeMachine.printStatus()
    }

    @Test
    fun `에이 그냥 안먹을래`() {
        coffeeMachine.order()
        coffeeMachine.printStatus()

        coffeeMachine.calc()
        coffeeMachine.printStatus()

        coffeeMachine.initialize()
        coffeeMachine.printStatus()
    }
}

 

반응형

'기술 용어' 카테고리의 다른 글

Swap memory  (0) 2022.11.27
서버 이중화 동작 방식  (0) 2022.09.11
데이터 레이크  (0) 2022.08.14
온프레미스(On-premise)  (0) 2022.08.12
C.A.P 이론  (0) 2022.07.16