Observer Pattern

rivermoon·2024년 10월 28일
post-thumbnail

Observer Pattern?

Observer Pattern은 객체 간 일대다 관계를 통해 한 객체의 상태가 변경될 때 이를 자동으로 통지하고 구독 중인 객체들이 변화에 반응하도록 하는 디자인 패턴이며, 핵심 구성요소로 Subject(주체)Observer(관찰자)가 있다.
Subject는 상태가 변할 때 Observer에게 변경 사항을 통지하고, Observer는 이를 자동으로 반응한다.
이 패턴은 데이터의 변화에 따라 UI 업데이트나 로직을 자동으로 수행하게 만들어 데이터의 일관성을 유지하고 코드간의 결합도를 낮출 수 있다.

Observer Pattern의 장단점

장점:

  • 느슨한 결합
    Subject와 Observer간 결합도를 낮춰 확장성을 보장한다.
  • 실시간 업데이트
    데이터 변경 시 자동으로 반영되므로 실시간 처리가 가능하다.
  • 재사용성
    Observer를 재사용해 다양한 객체에 적용할 수 있다.

단점:

  • 메모리 누수
    등록된 Observer가 제거되지 않으면 메모리 누수가 발생할 수 있다.
  • 복잡성
    Observer가 많아지면 관리가 복잡해질 수 있다.

안드로이드 개발에서 옵저버 패턴

Android에서는 LiveData, Flow, RxJava 등을 통해 Observer Pattern을 쉽게 구현할 수 있다.
특히, MVVM 구조에서 ViewModel이 LiveData를 통해 Activity나 Fragment와 데이터를 연동해 UI와 데이터 변경을 자동화하는 데 활용된다.
옵저버 패턴을 적용하면서 UI업데이트 로직을 간소화 하면서 메인스레드에서 안전하게 데이터 관찰이 가능해 효율적인 상태관리에 기여한다.

Observer Pattern 적용 예시 #1

class UserViewModel : ViewModel() {
    private val repository: UserRepository = UserRepository()

    val userFlow: Flow<List<User>> = repository.getUsers()
        .flowOn(Dispatchers.IO) // IO스레드에서 실행
        .catch { emit(emptyList()) } // 빈 리스트 방출
}

Flow는 Kotlin Coroutines에서 제공하는 비동기 스트림으로, Observer Pattern을 구현할 때 효과적이다.

class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        lifecycleScope.launch {
            viewModel.userFlow.collect { users ->
                userAdapter.submitList(users)
            }
        }
    }
}

Activity에서는 lifecycleScope.launch를 사용해 Flow를 수집하고(collect) 데이터가 변경될 때마다 UI를 업데이트한다.
lifecycleScope는 Activity의 생명주기에 맞춰 Flow를 구독하므로, Activity가 보이는 상태에서만 데이터를 수신하며, 자동으로 중지되어 메모리 누수를 방지한다.

즉 요약하자면,

  1. ViewModel에서 Flow로 데이터를 스트리밍하고 collect 메서드를 통해 Activity에서 구독
  2. lifecycleScope.launch로 Activity의 생명주기에 맞춰 Flow를 구독하여, Activity가 활성 상태일 때만 데이터를 수신

Observer Pattern 적용 예시 #2

네트워크 상태 모니터링

interface ConnectivityObserver {
    val networkStatus: Flow<NetworkStatus>
    enum class NetworkStatus { Available, Unavailable }
}

네트워크 상태 변화를 Flow 형태로 제공하기 위한 Interface

class ConnectivityObserverImpl(context: Context) : ConnectivityObserver {
    private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    
    override val networkStatus: Flow<NetworkStatus> = callbackFlow {
        val callback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                trySend(NetworkStatus.Available)
            }

            override fun onLost(network: Network) {
                trySend(NetworkStatus.Unavailable)
            }
        }
        connectivityManager.registerDefaultNetworkCallback(callback)
        awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
    }.distinctUntilChanged()
}

• ConnectivityObserverImpl은 ConnectivityManager를 통해 네트워크 상태를 callbackFlow로 변환하여 Flow 형태로 제공.
• 네트워크가 연결되면 onAvailable, 끊기면 onLost가 호출되어 NetworkStatus 값을 trySend로 전송.
• awaitClose로 callbackFlow가 닫힐 때 NetworkCallback을 해제해 리소스를 절약.
• distinctUntilChanged()로 동일 상태의 중복 업데이트를 방지.

class UserActivity : AppCompatActivity() {
    private val connectivityObserver: ConnectivityObserver = ConnectivityObserverImpl(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        lifecycleScope.launch {
            connectivityObserver.networkStatus.collect { status ->
                when (status) {
                    NetworkStatus.Available -> showOnlineUI()
                    NetworkStatus.Unavailable -> showOfflineUI()
                }
            }
        }
    }
}

• Activity는 lifecycleScope를 사용해 networkStatus를 구독.
collect를 호출해 networkStatus가 변화할 때마다 status 값을 수신하여 이를 기반으로 UI를 업데이트 처리.
• lifecycleScope는 Activity의 생명주기와 연동되어 있어, Activity가 종료되면 Flow 구독도 자동으로 해제되므로 메모리 누수를 방지.

즉 요약하자면,

  1. ConnectivityObserverImpl이 callbackFlow를 통해 네트워크 상태 변화를 Flow로 제공.
  2. collect를 통해 Activity가 이 상태 변화를 구독하고, 상태가 변경될 때마다 UI를 자동으로 업데이트.
  3. lifecycleScope를 통해 Activity 생명주기에 따라 자동으로 구독이 관리되어 메모리 누수 없이 안전하게 상태 변화 반영 가능.

결론

  • Observer Pattern은 데이터의 변화를 실시간으로 UI에 반영하고, 코드의 결합도를 낮춰 유지보수성을 높이는 필수적인 디자인 패턴이다.
  • LiveData, Flow, RxJava와 같은 도구?를 통해 구현하고, 생명주기를 인식해 메모리 누수를 방지하고 리소스를 최적화할 수 있다.
  • 옵저버 패턴의 주의사항과 최적화를 고려해 사용하면, 효율적인 반응형 애플리케이션을 구축할 수 있다.
profile
Android Developer

0개의 댓글