[디자인 패턴] 옵저버 패턴

Benji Android·2022년 7월 28일
0

디자인 패턴

목록 보기
3/7
post-thumbnail

정의

객체가 특정 객체 상태 변화를 감지하고 알림을 받는 패턴.
발생(publish) - 구독(subscribe) 패턴을 구현할 수 있다.
객체의 상태가 변경되면 Observer 의존하고 있는 객체에 업데이트된 상태를 알려주며,
일대다(one-to-many) 의존성 정의이다.


구현할 옵저버 패턴 클래스


옵저버 인터페이스 만들기

  • Observer
interface Observer {
		// update 함수의 생성자를 통해
		// 알려주고 싶은 상태를 생성자에 정의한다.
    fun update(temp: Float, humidity: Float, pressure: Float)
}
  • Subject
/**
 * 옵저버를 등록, 제거, 알림 기능을 제공하는 인터페이스이다.
 * 옵저버 패턴은 구독을하고 해제하는 함수를 항상 만들어 두어야 하며,
 * 필요가 없는 시점에 해제하지 않으면 메모리릭이 발생하기 때문에 사용하지 않는다면,
 * 꼭 해제를 명시적으로 해제를 시켜주어야 한다.
 * notifyObservers() 함수를 통해 구독된 옵저버 모두에게 변경된 상태를 알려 준다.
 */
interface Subject {
    fun registerObserver(o: Observer)
    fun removeObserver(o: Observer)
    fun notifyObservers()
}

옵저버 인터페이스와 옵저버를 등록하는 인터페이스를 만들었다.

다음으로는 Subject 정의하는 클래스를 만들어 보자.


옵저버 등록, 해제, 알림 클래스 만들기

class WeatherData : Subject {
    private var observers: ArrayList<Observer> = arrayListOf()
    private var temperature: Float = 0f
    private var humidity: Float = 0f
    private var pressure: Float = 0f

    override fun registerObserver(o: Observer) {
        observers.add(o)
    }

    override fun removeObserver(o: Observer) {
        observers.remove(o)
    }

    override fun notifyObservers() {
        observers.forEach {
            it.update(temperature, humidity, pressure)
        }
    }

    fun measurementsChanged() {
        notifyObservers()
    }

    fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) {
        this.temperature = temperature
        this.humidity = humidity
        this.pressure = pressure
        measurementsChanged()
    }
}
  • Subject 인터페이스를 등록한다.
  • 일대다(one-to-many) 구현을 위해 ArrayList<Observer> 생성한다.
  • 업데이트해 줄 데이터를 선언한다.
  • Subject 인터페이스의 매소드를 구현한다.
  • 상태가 변경되면 notifyObservers() 호출하여 구독하고 있는 사용자에게 알려준다.

registerObserver(Observer) 함수를 통해 ArrayList 에 구독자 정보를 저장하고 notifyObservers() 함수를 통해 ArrayList 에 저장되어 있는 모든 Observer 객체에게 데이터가 변경되 었다고 알려준다.

더 이상 구독된 정보를 받기를 원하지 않는 경우 또는 옵저버를 사용하지 않는 경우에 removeObserver(Observer) 함수를 통해 메모리에서 삭제한다.


옵저버 객체 만들기

class ForecastDisplay(
    weatherData: WeatherData
) : Observer, DisplayElement {
    private var currentPressure = 29.92f
    private var lastPressure: Float = 0f

    init {
        weatherData.registerObserver(this)
    }

    override fun display() {
        println("Forecast: ")
        if (currentPressure > lastPressure) {
            println("Improving weather on the way!")
        } else if (currentPressure == lastPressure) {
            println("More of the same")
        } else if (currentPressure < lastPressure) {
            println("Watch out for cooler, rainy weather")
        }
    }

    override fun update(temp: Float, humidity: Float, pressure: Float) {
        lastPressure = currentPressure
        currentPressure = pressure
        display()
    }
}
  • 생성자에 Subject 가 만들어져 있는 클래스를 받는다.
  • Observer 인터페이스를 상속하여 update() 함수를 정의한다.
  • init 함수를 통해 구독 요청을 보낸다.
  • notifyObservers() 함수가 호출 되면 update() 정의해둔 로직이 실행 되어 데이터의 상태가 변경 된다.

옵저버 실행 하기

fun main() {
    val weatherData: WeatherData = WeatherData()

    val currentDisplay = CurrentConditionsDisplay(weatherData)
    val statisticsDisplay = StatisticsDisplay(weatherData)
    val forecastDisplay = ForecastDisplay(weatherData)

    println("첫 번째 데이터")
    weatherData.setMeasurements(85f, 65f, 30.4f)
    println("두 번째 데이터")
    weatherData.setMeasurements(82f, 70f, 29.2f)
    println("세 번째 데이터")
    weatherData.setMeasurements(78f, 90f, 29.2f)

    weatherData.removeObserver(forecastDisplay)
    println("\nForecastDisplay 구독 취소 후 데이터")
    weatherData.setMeasurements(62f, 90f, 28.1f)
}
  • Subject 생성하는 WeatherData 클래스를 초기화 한다.
  • 구독을 원하는 Observer 상속되어 있는 클래스를 초기화 한다.
  • setMeasurements() 함수를 통해 구독자에게 데이터가 변경되었음을 알려준다.
  • forecastDisplay 객체의 구독을 제거한다.
  • 그 이후 데이터는 어떻게 나오게 될까? 확인해 보자.

결과

ForecastDisplay 구독을 취소 후 데이터를 변경하면,

기존에 CurrentConditionsDisplay, StatisticsDisplay의 데이터만 변경되고

ForecastDisplay는 더 이상 데이터 변화를 감지할 수 없다.


특징

  • 장점
    • 상태를 변경하는 객체(Publisher)와 변경을 감지하는 객체(Subsriber)의 관계를 느슨하게 유지할 수 있다.
    • Subejct의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지할 수 있다.
    • 런타임에 옵저버를 추가하거나 제거할 수 있다.
  • 단점
    • 복잡도가 증가한다.
    • 다수의 Observer 객체를 등록 이후 해지하지 않는다면 메모리릭이 발생할 수도 있다.

참고

profile
Android 주니어 개발자

0개의 댓글