본문으로 바로가기

Reactive Programming #0. 옵저버 패턴

category Programming 2022. 1. 17. 00:49

Reactive Programming #0. 옵저버 패턴

목차

    옵저버 패턴이란?

    • 어떠한것들을 감지하고, 상태의 변경을 지켜보는 디자인 패턴.
    • 여러개의 객체가 어떠한 특정 상태의 변경을 감지하고 반응을 해야하는 경우 적용.
    • Pub/Sub 패턴을 쉽게 구현 가능.

    채팅 서버/클라이언트 기반 예시 (옵저버 패턴 적용 전)

    ChatServer 클래스

    class ChatServer(
        private val messages: MutableMap<String, MutableList<String>> = mutableMapOf()
    ) {
    
        fun add(subject: String, message: String) {
            messages[subject]?.let {
                it.add(message)
            }?: kotlin.run {
                val messageList = mutableListOf<String>()
                messageList.add(message)
                messages.put(subject, messageList)
            }
        }
    
        fun getMessage(subject: String): MutableList<String>? {
            return messages[subject]
        }
    }

    User 클래스

    class User(
        private val chatServer: ChatServer
    ) {
    
        fun sendMessage(subject: String, message: String) {
            chatServer.add(subject, message)
        }
    
        fun getMessage(subject: String): MutableList<String>? {
            return chatServer.getMessage(subject)
        }
    }

    Client 메인 함수

    fun main() {
        val chatServer = ChatServer()
    
        val user1 = User(chatServer)
        user1.sendMessage("디자인패턴", "이번엔 옵저버 패턴입니다.")
        user1.sendMessage("롤드컵2021", "LCK 화이팅!")
    
        val user2 = User(chatServer)
        println(user2.getMessage("디자인패턴"))
    
        user1.sendMessage("디자인패턴", "예제 코드 보는 중..")
        println(user2.getMessage("디자인패턴"))
    }

     

    user1이 메세지를 보냈을때 user2는 계속해서 메세지를 가져와야 함.

    위 경우에는 user2가 메세지를 받으려고 계속해서 요청을 보내는 형태. (Calling 매커니즘)

    이러한 경우 메세지가 존재하면 좋겠지만 메세지가 존재하지 않을 경우도 있기 때문에 해당 애플리케이션에 맞지 않음.

     

    -> 이럴때 옵저버 패턴을 사용한다.

     

    옵저버 패턴 좀 더 자세하게!

    다수의 객체가 특정 객체 상태 변화를 감지하고 알림 받는 패턴.

    Subject

    여러 옵저버들을 등록하거나 해지 할 수 있는 기능을 제공.

    Subject의 상태가 변경되면 자신에게 등록 된 모든 옵저버를 순회하며 옵저버가 제공하는 모든 메서드를 호출.

    채팅 서버/클라이언트 기반 예시 (옵저버 패턴 적용 후)

    ChatServer 클래스 (Subject 역할)

    class ChatServer(
        private val _subscribers: MutableMap<String, MutableList<Subscriber>> = mutableMapOf()
    ) {
    
        fun register(subject: String, subscriber: Subscriber) {
            _subscribers[subject]?.let {
                it.add(subscriber)
            }?: kotlin.run {
                val subscribers = mutableListOf<Subscriber>()
    
                subscribers.add(subscriber)
                _subscribers.put(subject, subscribers)
            }
        }
    
        fun unregister(subject: String, subscriber: Subscriber) {
            _subscribers[subject]?.let {
                it.remove(subscriber)
            }
        }
    
        fun sendMessage(user: User, subject: String, message: String) {
            _subscribers[subject]?.forEach {
                val receiver = it as? User
    
                it.handleMessage("${user.name} -> ${receiver?.name} : $message")
            }
        }
    }

    Subscriber 인터페이스 (Observer 인터페이스 역할)

    interface Subscriber {
    
        fun handleMessage(message: String)
    }

    User 클래스 (ConcreteObserver 역할)

    class User(
        val name: String
    ): Subscriber {
    
        override fun handleMessage(message: String) {
            println(message)
        }
    }

    Client 메인 함수

    fun main() {
    
        val chatServer = ChatServer()
    
        val user1 = User("u1")
        val user2 = User("u2")
    
        chatServer.register("코틀린", user1)
        chatServer.register("코틀린", user2)
    
        chatServer.register("RX", user1)
    
        chatServer.sendMessage(user1, "코틀린", "코틀린 꿀잼?")
        chatServer.sendMessage(user1, "RX", "리액티브 스트림 어려워?")
    }

    옵저버 패턴의 장/단점

    장점

    • 상태를 변경하는 객체(publisher)와 변경을 감지하는 객체(subscriber)의 관계를 느슨하게 유지할 수 있음.
    • Subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지할 수 있음.
    • 런타임에 옵저버를 추가하거나 제거할 수 있음.

    단점

    • 복잡도가 증가.
    • 다수의 Observer 객체를 등록 이후 해지 하지 않으면 Memory Leak이 발생.

    자바에 적용된 옵저버 패턴

    • Java 9 이전
      • Observer 인터페이스 (Java 9 부터 사용 권장하지 않음.)
      • Observable 클래스 (Java 9 부터 사용 권장하지 않음.)
    • Java 9 이후
      • java.beans
        • PropertyChangeListener
        • PropertyChangeSupport
      • Flow API (Reactive Stream을 구현할 수 있도록 제공되는 인터페이스)
        • Subscription
        • Subscriber
        • Publisher
        • 해당 인터페이스 만으로는 동기적으로 작동함. 비동기적으로 사용하려면 SubmissionPublisher와 같이 사용해야 함.
        • 구독을 하는쪽에서 백프레셔를 제어 가능.

    참고

    https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4

    'Programming' 카테고리의 다른 글

    Reactive Programming #1. Reactive Streams  (0) 2022.01.21