Gof의 디자인 패턴 - 옵저버 패턴

Groot·2024년 8월 4일
0

TIL

목록 보기
150/153
post-thumbnail
post-custom-banner

옵저버 패턴

  • 객체 사이에 일 대 가의 의존 관계를 정의해 두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 전달받고 자동으로 갱신될 수 있게 만든다.

활용성

  • 어떤 추상 개념이 두 가지 양상을 갖고 하나가 다른 하나에 종속적일 때, 각 양상을 별도의 객체로 캡슐화하여 이들 각각 재사용할 수 있다.
  • 한 객체에 가해진 변경으로 다른 객체를 변경해야 하고, 프로그래머들은 얼마나 많은 객체들이 변경되어야 하는지 몰라도 될 때
  • 어떤 객체가 다른 객체에 자신의 변화를 통보할 수 있는데, 그 변화에 관심있어 하는 객체들이 누구인지에 대한 가정 없이도 통보가 될 때

구조

요소

  • Subject : 감시자들을 알고 있느 주체, 감시자 객체를 붙이거나 떼는 데 필요한 인터페이스 제공
  • Observer : 주체에 생긴 변화에 관심 있는 객체를 갱신하는 데 필요한 인터페이스 정의, 주체의 변경에 따라 변화되어야 하는 객체들의 일관성 유지
  • ConcreteSubJect : 객체에게 알려줘야 하는 상태저장
  • ConcreteObserver : 객체에 대한 참조자 관리, 주체의 상태와 일관성을 유지해야 하는 상태를 저장, 갱신 인터페이스 구현

협력 방법

  • ConcreteSubJect는 Observer의 상태와 자신의 상태가 달라지는 변경이 발생할 때마다 감시자에게 통보한다.
  • ConcreteSubject에서 변경이 통보된 후, ConcreteObserver는 필요한 정보를 주체에게 질의하여 얻어온다. ConcreteObserver는 이 정보를 이용해서 주체의 상태와 자신의 상태를 일치시킨다.

장점

  • 결합이 느슨해진다. Subject와 Observer 사이에 추상적인 결합도만이 존재
    • 주체가 아는것은 감시자들의 리스트일 뿐이다.
    • 이 감시자들은 Observer의 인터페이스만 따른다
  • 브로드캐스트 방식의 교류를 가능하게 한다.
    • 주체는 얼마나 많은 객체들이 이 변화 정보를 원하는지 알 필요가 없고, 단지 자신의 감시자에게만 상태 변화를 알려주면 감시자가 이 통보를 처리할지 무시할지를 결정한다.

단점

  • 예측하지 못한 정보를 갱신한다.
    • 감시자는 다른 감시자의 존재를 모르기 때문에 주체를 변경하는 비용에 대해 모른다.
    • 감시자가 많으면 연속적인 수정이 발생할 수 있다.
    • 추적이 어려울 수도 있다.
    • 감시자가 무엇이 변경되었는지 알 수 있게 해 주는 별도의 프로토콜이 추가되면 변경을 유추할 수 있다.

예시 코드

import Foundation

// Observer 프로토콜 정의
// 옵저버는 이 프로토콜을 준수하여 업데이트 메서드를 구현해야 합니다.
protocol Observer: AnyObject {
    func update(temperature: Float, humidity: Float)
}

// Subject 프로토콜 정의
// 서브젝트는 옵저버를 등록하고 제거하며, 알림을 보낼 수 있어야 합니다.
protocol Subject: AnyObject {
    func registerObserver(_ observer: Observer)
    func removeObserver(_ observer: Observer)
    func notifyObservers()
}

// WeatherStation 클래스 정의
// WeatherStation은 Subject 프로토콜을 준수하고, 상태를 관리합니다.
class WeatherStation: Subject {
    private var observers = [Observer]()
    private var temperature: Float = 0.0
    private var humidity: Float = 0.0
    
    // 옵저버를 등록합니다.
    func registerObserver(_ observer: Observer) {
        observers.append(observer)
    }
    
    // 옵저버를 제거합니다.
    func removeObserver(_ observer: Observer) {
        if let index = observers.firstIndex(where: { $0 === observer }) {
            observers.remove(at: index)
        }
    }
    
    // 모든 옵저버에게 업데이트를 알립니다.
    func notifyObservers() {
        for observer in observers {
            observer.update(temperature: temperature, humidity: humidity)
        }
    }
    
    // 상태를 설정하고 변경을 알립니다.
    func setMeasurements(temperature: Float, humidity: Float) {
        self.temperature = temperature
        self.humidity = humidity
        measurementsChanged()
    }
    
    // 상태 변경 시 옵저버들에게 알립니다.
    private func measurementsChanged() {
        notifyObservers()
    }
}

// CurrentConditionsDisplay 클래스 정의
// 현재 상태를 표시하는 옵저버입니다.
class CurrentConditionsDisplay: Observer {
    private var temperature: Float = 0.0
    private var humidity: Float = 0.0
    
    // 상태 업데이트 시 호출됩니다.
    func update(temperature: Float, humidity: Float) {
        self.temperature = temperature
        self.humidity = humidity
        display()
    }
    
    // 현재 상태를 출력합니다.
    func display() {
        print("Current conditions: \(temperature)F degrees and \(humidity)% humidity")
    }
}

// ForecastDisplay 클래스 정의
// 예보를 표시하는 옵저버입니다.
class ForecastDisplay: Observer {
    private var currentPressure: Float = 29.92
    private var lastPressure: Float = 0.0
    
    // 상태 업데이트 시 호출됩니다.
    func update(temperature: Float, humidity: Float) {
        lastPressure = currentPressure
        currentPressure = temperature // 간단한 예로 압력을 온도로 대체했습니다.
        display()
    }
    
    // 예보를 출력합니다.
    func display() {
        if currentPressure > lastPressure {
            print("Forecast: Improving weather on the way!")
        } else if currentPressure == lastPressure {
            print("Forecast: More of the same")
        } else {
            print("Forecast: Watch out for cooler, rainy weather")
        }
    }
}

// WeatherStation 객체 생성
let weatherStation = WeatherStation()
    
// CurrentConditionsDisplay 및 ForecastDisplay 객체 생성
let currentDisplay = CurrentConditionsDisplay()
let forecastDisplay = ForecastDisplay()
    
// 옵저버 등록
weatherStation.registerObserver(currentDisplay)
weatherStation.registerObserver(forecastDisplay)
    
// 상태 변경
weatherStation.setMeasurements(temperature: 80, humidity: 65)
weatherStation.setMeasurements(temperature: 82, humidity: 70)
    weatherStation.setMeasurements(temperature: 78, humidity: 90)
    
}

참고

profile
I Am Groot
post-custom-banner

0개의 댓글