동일한 이벤트에 대해 최초와 그 이후를 다르게 처리하기

어떤 이벤트에 대해, 그 이벤트가 최초로 발생했을 때와 그 이후에 발생했을 때를 다르게 처리해야하는 경우가 있다.

보통은 이럴 때 isFirst 같은 변수를 하나 만들어두고 이벤트 핸들러에서 분기처리하는 식으로 구현하는데, 이벤트에 대한 처리를 객체로 캡슐화하면 조건문을 제거할 수 있다.

변수를 사용하는 방식

private var isFirst = true

private fun handleEvent() {
    if (isFirst) {
        isFirst = false
        // 최초 발생 시 처리할 것들
    } else {
        // 두 번째부터 처리할 것들
    }
}

// 이벤트가 발생하면 호출되는 메서드
fun onEvent() {
    handleEvent()
}

객체로 캡슐화한 방식

아래는 메서드에 대한 참조 변수를 사용하여 구현한 것이다.

private fun handleEventFirstTime() {
    eventHandler = ::handleEvent  // 이후부터는 handleEvent를 호출하도록
    // 최초 발생 시 처리할 것들
}

private fun handleEvent() {
    // 두 번째부터 처리할 것들
}

private var eventHandler = ::handleEventFirstTime  // 최초의 이벤트를 처리하는 핸들러로 초기화

// 이벤트가 발생하면 호출되는 메서드
fun onEvent() {
    eventHandler()
}

이렇게 하면 최초에 할당되어있던 handleEventFirstTime이 자신을 호출한 eventHandler의 참조를 변경하게 되어, 이후부터는 eventHandler를 invoke 하면 handleEvent가 호출된다.

메서드 참조를 사용한 방식이 아니라 객체로 캡슐화한 방식이라고 소개한 이유는, 우선 Java와 Kotlin에서는 메서드 역시 내부적으로는 객체로 구현되며, 다른 객체지향 언어로 구현하고자 하더라도 동일한 기능을 하는 클래스를 선언해서 구현할 수 있기 때문이다. 객체지향 언어가 아닌 언어, 예를 들어 C언어로도 함수포인터를 사용하면 구현할 수 있다. 객체지향은 패러다임이지 언어나 문법을 말하는 것이 아니다. Java로도 절차지향적 프로그래밍을 할 수 있고 C언어로도 객체지향적 프로그래밍을 할 수 있다.

사실 이것은 상태(State) 패턴을 구현한 것이다

사실 위와 같은 구현은 상태가 두 개인 상태 패턴을 구현한 것이다. 디자인패턴을 설명하는 자료들을 보면 각 상태를 나타내는 인터페이스를 선언하고 이를 구현하는 클래스들을 작성하는 식으로 되어있지만, 이는 패턴의 핵심 아이디어를 명확하게 전달하기 위함일 뿐 반드시 거기에 나와있는 클래스 다이어그램에 꼭 맞춰서 구현할 필요는 없다.

런타임에 Behavior를 바꾸는 비슷한 패턴으로 전략(Strategy) 패턴이 있다. 전략 패턴은 객체의 외부에서 객채의 행동을 선택할 수 있도록 하는 패턴이다. 코드만 놓고 보면 거의 똑같지만 의도와 목적이 다르고 여기에서 미묘한 차이가 존재한다. 이 예제는 이벤트 핸들러를 구현하는 객체 내부에서 자신의 상태에 따라 이벤트를 처리하는 것이고 이를 외부에서 알 필요는 없으므로 전략 패턴이 아닌 상태 패턴으로 구현하는 것이 적절하다.

Leave a Comment

Your email address will not be published.