본문 바로가기
Kotlin/Design Pattern

[TIL] Observer Pattern

by 명훈스토리 2023. 1. 17.
SMALL

1. 옵저버 패턴이란?(Observer Pattern)

 옵저버 패턴은 리액티브 프로그래밍의 기초가 되는 패턴이다. Observer(관찰자)는 데이터의 변화를 관찰하며, 관찰한 변화를 필요한 곳에 알린다. 따라서 옵저버 패턴은 기본적으로 발행자와 구독자 둘로 구성된다. 발행자는 변화하는 데이터이며 구독자는 데이터의 변화를 관찰해 필요한 동작을 수행한다.

 과거 명령형 프로그래밍 패러다임에서는 데이터가 변할 때 그에 따라 변해야 하는 부분에 모두 적용시켜줘야 했다. 이를 보완하기 위해 리액티브 프로그래밍 패러다임인 옵저버 패턴이 만들어졌다.

2. 옵저버 패턴 사용

 데이터 변경 시 어떻게 옵저버 패턴이 동작하는가? 옵저버 패턴은 데이터 변경 시 이전 값과 변화한 값을 인자로 받는 메서드를 호출하는 방식으로 동작한다. RxJava의 Observable, Coroutines의 Flow, Kotlin의 ObserverbleProperty 등 모두 같은 방식으로 구현되며 직관적인 이해를 위해 아래 예제를 살펴보자.

RxJava 옵저버 패턴

Observable => 발행자 생성

subscribe => 구독자

val publisher: Observable<String> = Observable.create { emitter: ObservableEmitter<String> ->
    emitter.onNext("hello")
    emitter.onNext("kotlin")
    emitter.onComplete()
}

publisher.subscribe {
    println(it)
}

 위 코드에서 publisher가 hello와 kotlin 인자를 발행하고 subscriber는 해당 값을 구독하여 println()을 실행하고 있다.

 

Coroutine 옵저버 패턴

Flow => 발행자

collect => 구독자

val flow = flow {
    emit("abc")
    emit("def")
}

CoroutineScope(Dispatchers.IO).launch {
    flow.collect {
        println(it)
    }
}

 

// Activity의 onCreate()에서 실행
CoroutineScope(Dispatchers.IO).launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        flowOfStrings.collect { data ->
            Log.e(TAG, "flowOfStrings : $data")
        }
        printNumbers().collect { data ->
            Log.e(TAG, ">> printNumbers() : $data")
        }
    }
}

// 멤버변수로 선언
private val flowOfStrings = flow {
    for (number in 0..10) {
        emit("Emitting: $number")
    }
}

// 클래스 안의 메소드로 선언
private fun printNumbers(): Flow<Int> = flow {
    for (i in 1..10) {
        emit(i) // emit(value: Int)
        Log.e(TAG, "$i emit됨")
    }
}

 

 위 코드에서 flow가 발행자, collect 함수로 구독자 역할을 수행한다.

 

Kotlin Delegates.observable

Kotlin Delegates.observable은 시작값, 동작 람다식을 인자로 받는다. 값(data)가 발행자, 동작 람다식이 구독자이다.

var data: String by Delegates.observable(
    initialValue = "",

    // onChange => 구독자
    onChange = { property: KProperty<*>, oldValue: String, newValue: String ->
        println("Data Changed >> from $oldValue to $newValue")
    }
)

data = "a"
data = "b"
data = "c"

data => 발행자

onChanged 람다식 => 구독자

RxJava, Coroutine과 다르게 발행자와 구독자가 한 번에 붙여진다. 또한, Delegates.observable은 하나의 구독자만 붙일 수 있고, RxJava와 Coroutine은 하나의 발행자에 여러 구독자를 붙일 수 있다.

 

3. 결론

옵저버 패턴은 데이터의 변화를 관찰하기 위한 패턴이다.

LIST

댓글