<옵저버 Observer>
옵저버는 '이벤트가 일어나는 것을 감시'하는 감시자의 역할을 만듭니다
안드로이드에서는 키의 입력, 터치의 발생, 데이터의 수신 등 함수로 직접 요청하지 않았지만
시스템 또는 루틴에 의해서 발생하게 되는 동작들을 '이벤트'라고 부르며 이 이벤트가 발생할 때마다
'즉각적으로 처리'할 수 있도록 만드는 프로그래밍 패턴을 '옵저버 패턴' 이라고 부릅니다
옵저버 패턴을 구현할 때는 2개의 클래스가 필요합니다
하나는 이벤트를 수신하는 클래스(class A), 하나는 이벤트를 발생시키고 이를 알려주는 클래스(class B)입니다
각각의 클래스를 A, B 라고 두었을 때,
B에서 이벤트가 발생할 때 A에 있는 이벤트를 처리하는 함수를 호출하여 알려줍니다
이때 B에서는 자신의 이벤트를 받을 수 있는 인터페이스를 만들어 공개하고
A는 B를 구현하여 B에 넘겨주면 인터페이스만 알아도 이벤트를 넘겨줄 수 있게 됩니다
(B에 이벤트가 발생하면 종을 쳐서 A에게 이벤트가 발생했다는 것을 알려주는 것으로 보면 쉽습니다!
이때 종의 역할을 하는것이 인터페이스!)
옵저버 패턴을 이용하면 다른 객체의 상태 변화를 별도의 함수 호출 없이 즉각적으로 알 수 있기 때문에
이벤트에 대한 처리가 자주 이루어져야 하는 경우 유용하게 사용할 수 있습니다
이때 이 인터페이스를 'observer' 또는 코틀린에서는 'listener'라고 부르며
이렇게 이벤트를 넘겨주는 행위를 'callback' 이라고 합니다
이제 코드 예시를 통해 알아보도록 하겠습니다
이벤트를 수신하여 출력하는 EventPrinter와 숫자를 카운트하며 5의 배수마다 이벤트를 발생시킬 Counter,
그리고 이 두개를 연결시킬 인터페이스인 EventListener로 이루어져 있습니다
fun main() {
EventPrinter().start()
}
interface EventListener{ // 이벤트가 발생할 때 숫자를 반환해주는 인터페이스
fun onEvent(count:Int)
}
class Counter (var listener: EventListener) { // 이벤트가 발생되는 클래스
fun count() {
for (i in 1..100) {
if (i % 5 == 0) listener.onEvent(i) // 5의 배수가 될 때마다 EventListener의 onEvent 호출
}
}
}
class EventPrinter : EventListener { // 이벤트를 받아서 화면에 출력할 클래스
override fun onEvent(count: Int) {
print("${count}-")
}
fun start() {
val counter = Counter(this)
counter.count()
}
}
/* 출력
5-10-15-20-25-30-35-40-45-50-55-60-65-70-75-80-85-90-95-100-
*/
첫번째로 EventLister 인터페이스가 있는데요, 이 인터페이스는 이벤트가 발생할 때 숫자를 반환해 줍니다
안에는 onEvent라는 추상함수를 만들고 파라미터로 정수값을 넘겨줍니다
*리스너를 통해 이벤트를 반환하는 함수 이름은 관례적으로 'on(행위)' 라는 규칙을 따릅니다
두번째는 이벤트가 발생되는 Counter 클래스입니다
이벤트를 발생시키기 위해 생성자에서 EventListener를 속성으로 받습니다
그리고 count라는 함수를 호출하면 1에서 100까지 반복하면서 5의 배수가 될 때마다 EventListener의 onEvent를 호출합니다
(이벤트 발생시마다 전달받은 리스너의 메소드 호출!)
세번째는 이벤트를 받아서 화면에 출력할 EventPrinter 클래스입니다
EventPrinter는 EventListener를 상속하여 구현하였습니다
onEvent 함수를 override 하여 받아온 숫자와 대시문자를 출력합니다
또, start라는 함수도 추가하였는데요
먼저 Counter의 인스턴스를 만들되 this라는 키워드로 EventListener 구현부를 넘겨줍니다
여기서 this는 EventPrinter 객체 자신을 나타내지만 받는 쪽에서 'EventListener'만 요구했기 때문에
EventListener 구현부만 넘겨주게 됩니다
(이를 객체지향의 다형성이라고 하는데요 상속받아 만들어진 클래스는 슈퍼클래스의 기능을 포함하여 제작되었으므로
슈퍼클래스에서 정의한 부분만 따로 넘겨줄 수 있습니다)
그리고 count를 시작시켜줍니다
마지막으로 main에서는 EventPrinter의 인스턴스를 생성하고 start 함수를 호출해줍니다
그리고 출력결과를 보면 Counter가 5의 배수마다 이벤트를 발생시킨 것을 EventPrinter 내에서 구현된 EventListener 에서
출력하고 있음을 확인할 수 있습니다
그런데 EventPrinter가 EventListener를 상속받아 구현하지 않고 임시로 만든 별도의 EventListener 객체를 대신 넘겨줄 수도 있는데요
이것을 '이름이 없는 객체'라고 하여 익명객체 Anonymous Object 라고 부릅니다
<익명 객체 Anonymous Object>
위에서 작성했던 코드를 조금 수정해보도록 하겠습니다
fun main() {
EventPrinter().start()
}
interface EventListener { // 이벤트가 발생할 때 숫자를 반환해주는 인터페이스
fun onEvent(count:Int)
}
class Counter (var listener: EventListener) { // 이벤트가 발생되는 클래스
fun count() {
for (i in 1..100) {
if (i % 5 == 0) listener.onEvent(i) // 5의 배수가 될 때마다 EventListener의 onEvent 호출
}
}
}
class EventPrinter {
fun start() {
val counter = Counter(object: EventListener {
override fun onEvent(count: Int) {
print("${count}-")
}
})
counter.count()
}
}
/* 출력
5-10-15-20-25-30-35-40-45-50-55-60-65-70-75-80-85-90-95-100-
*/
EventListener, Counter 코드는 똑같고 EventPrinter 클래스만 다시 작성하였습니다
아까 코드와 달리 EventListener를 상속받지 않고 바로 start 함수를 만들었습니다
그리고 Counter 인스턴스를 만들 때 파라미터에 익명객체를 만들어 넘겨주었는데요
이때 object를 사용하여 만들어주었습니다
object 키워드 뒤에 이름없이 바로 콜론을 쓴 후 EventListener를 상속 받도록 하고
중괄호를 친 후 onEvent 함수를 override 해주었습니다
위에서 사용한 object의 특징은 이름이 없다는 것인데요
이렇게 만들면 인터페이스를 구현한 객체를 코드 중간에도 '즉시 생성'하여 사용할 수 있습니다
그리고 count를 시작하도록 하였습니다
출력 결과를 보면 아까 위의 코드와 똑같이 출력되는 것을 확인할 수 있습니다
observer 패턴은 이벤트를 기반으로 동작하는 모든 코드에서 광범위하게 쓰이는 방식입니다!
<익명객체와 옵저버 패턴> 포스팅은 여기서 마치도록 하겠습니다
'Kotlin' 카테고리의 다른 글
[Kotlin] 제너릭 Generic (0) | 2023.08.18 |
---|---|
[Kotlin] 클래스의 다형성 (0) | 2023.08.03 |
[Kotlin] 오브젝트 Object (0) | 2023.07.27 |
[Kotlin] 스코프 함수 (0) | 2023.07.26 |
[Kotlin] 고차함수와 람다함수 (0) | 2023.07.25 |