Menu
Coddy logo textTech

옵저버 패턴

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 94번째.

옵서버 패턴은 objects 간의 일대다 의존성을 정의합니다. 한 object(subject)의 상태가 변경되면 모든 종속 객체(옵서버)에 자동으로 notified됩니다. 이는 이벤트 시스템, UI 업데이트 또는 여러 객체가 변경 사항에 반응해야 하는 모든 상황에 이상적입니다.

이 패턴에는 두 가지 핵심 역할이 있습니다. 관찰자 목록을 유지하고 이들에게 알림을 보내는 Subject와 업데이트 인터페이스를 구현하는 Observers입니다.

#include <iostream>
#include <vector>
#include <algorithm>

class Observer {
public:
    virtual void update(int value) = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<Observer*> observers;
    int state = 0;
    
public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }
    
    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }
    
    void setState(int value) {
        state = value;
        notify();
    }
    
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }
};

class Display : public Observer {
    std::string name;
public:
    Display(const std::string& n) : name(n) {}
    void update(int value) override {
        std::cout << name << " received: " << value << "\n";
    }
};

int main() {
    Subject sensor;
    Display screen1("Screen1"), screen2("Screen2");
    
    sensor.attach(&screen1);
    sensor.attach(&screen2);
    sensor.setState(42);  // 두 디스플레이 모두 알림을 받음
}

setState()가 호출되면 주체는 등록된 모든 옵저버를 순회하며 해당 옵저버의 update() 메서드를 호출합니다. 옵저버는 언제든지 연결하거나 연결을 해제할 수 있으므로 시스템이 유연하고 느슨하게 결합됩니다.

object의 변경 사항으로 다른 objects를 업데이트해야 하지만 이러한 objects를 서로 강하게 결합하고 싶지 않을 때 Observer를 사용하세요.

challenge icon

챌린지

쉬움

Observer 패턴을 사용하여 Weather Station 모니터링 시스템을 만들어 봅시다. 온도 데이터를 추적하고 온도가 변경될 때마다 여러 디스플레이 장치에 자동으로 알림을 보내는 기상 관측소를 만들 것입니다. 이는 이 패턴의 전형적인 실제 활용 사례입니다.

코드를 세 개의 파일로 구성합니다:

  • Observer.h: observer 인터페이스와 구체적인 디스플레이 observer를 define합니다.

    순수 가상 update(double temperature) method를 포함하는 추상 Observer class를 Create합니다. observers는 알림을 받기 위해 이 method를 Implement하며, virtual destructor도 함께 포함합니다.

    그런 다음 Observer에서 상속하는 TemperatureDisplay class를 Create합니다. each 디스플레이에는 constructor를 통해 설정되는 name(string)이 있습니다. update()가 호출되면 다음을 출력해야 합니다:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: temperature를 유지하고 observers에게 알림을 보내는 subject를 Create합니다.

    WeatherStation class는 다음을 수행해야 합니다:

    • observer 포인터의 list와 current temperature를 저장합니다(0.0으로 initialize)
    • observers를 등록하는 attach(Observer* obs) method를 가집니다
    • observers를 제거하는 detach(Observer* obs) method를 가집니다
    • temperature를 업데이트하고 attach된 모든 observers에게 알림을 보내는 setTemperature(double temp) method를 가집니다
    • 각 observer에서 current temperature와 함께 update()를 호출하는 private notify() method를 가집니다
  • main.cpp: Observer 패턴이 실제로 동작하는 모습을 보여 줍니다.

    세 개의 input을 읽습니다:

    1. 첫 번째 디스플레이의 name(string)
    2. 두 번째 디스플레이의 name(string)
    3. temperature 값(double)

    제공된 names를 사용하여 WeatherStation 하나와 TemperatureDisplay objects 두 개를 Create합니다. 두 디스플레이를 station에 attach한 다음 temperature를 input 값으로 설정합니다. 두 디스플레이 모두 자동으로 update를 받고 messages를 출력해야 합니다.

    그 후 첫 번째 디스플레이를 detach하고 input보다 5 degrees 높은 새 temperature를 설정합니다. 두 번째 디스플레이만 이 update를 받아야 합니다.

예를 들어, inputs가 Kitchen, Bedroom, 22.5인 경우:

Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees

inputs가 Office, Lobby, 18.0인 경우:

Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees

Observer 패턴이 어떻게 느슨하게 결합된 시스템을 만드는지 확인해 보세요. weather station은 특정 디스플레이에 대해 알 필요 없이, 단지 listening 중인 대상에게 notify하기만 합니다. observer를 detach하면 해당 observer는 자동으로 updates를 더 이상 받지 않습니다.

직접 해보기

#include <iostream>
#include <string>
#include "WeatherStation.h"

using namespace std;

int main() {
    // 입력 읽기
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: WeatherStation 객체 생성
    
    // TODO: 입력 이름으로 두 개의 TemperatureDisplay 객체 생성
    
    // TODO: 두 디스플레이를 기상 관측소에 연결
    
    // TODO: 온도를 입력 값으로 설정
    // (두 디스플레이 모두 메시지를 출력해야 함)
    
    // TODO: 첫 번째 디스플레이 분리
    
    // TODO: 온도를 입력보다 5도 높게 설정
    // (두 번째 디스플레이만 출력해야 함)
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러