Skip to content
Kang Log
Go back

옵저버패턴

Updated:

변화하는 부분을 포착하기

기존 방식대로라면 코드 구현은 아래와 같다

void measurementsChanged() {
  double temp = getTemperature();
  double humidity = getHumidity();
  double pressure = getPressure();

  // 값이 변하면 일일이 전부 알림 
  currentConditionDisplay.update(temp, humidity, pressure);
  statisticsDisplay.update(temp, humidity, pressure);
  forecaseDisplay.update(temp, humidity, pressure);
}

위 코드의 문제점은 구체적인 구현에 맞춰 코딩(하드코딩)했으므로 프로그램을 고치지 않고서는 다른 디스플레이 항목을 추가하거나 제거할 수 없다.

그리고 디스플레이 항목과 데이터를 주고 받는데 공통된 인터페이스를 사용하고 있다.

따라서 위 부분을 캡슐화를 진행해야한다 -> 옵저버 패턴 사용

2026-06-30-001

느슨한 결함(Loose Coupling)

객체들이 상호작용할 수 있지만, 서로를 잘 모르는 관계를 의미 한다. 느슨한 결합을 활용하면 유연성이 아주 좋아진다.

옵저버 패턴에서 느슨한 결합을 만드는 방식

디자인 원칙

  1. 바뀌는 부분은 캡슐화한다.
  2. 상속보다는 구성을 활용한다.
  3. 구현보다는 인터페이스에 맞춰서 프로그래밍한다.
  4. 상호작용하는 개체 사이에서는 가능하면 느슨한 결합을 사용해야 한다.

옵저버 패턴

한 객체(Subject)의 상태가 바뀌면 그 객체에 의존하는 다른 객체(Observer)에게 연락이 가고 자동으로 내용을 갱신되는 방식으로 일대다 의존성을 정의합니다.

2026-06-30-002

C++로 알아보는 간단한 예시

// Observer 인터페이스
class Observer {
public:
  virtual void update(const string& news) = 0;
  virtual ~Observer() = default;
};

// Subject 인터페이스
class Subject {
public:
  virtual void addObserver(shared_ptr<Observer> observer) = 0;
  virtual void removeObserver(shared_ptr<Observer> observer) = 0;
  virtual void notifyObservers() = 0;
  virtual ~Subject() = default;
};
// 구체적인 Subject
class NewsAgency : public Subject {
private:
  vector<weak_ptr<Observer>> observers;
  string latestNews;

public:
  void addObserver(shared_ptr<Observer> observer) override {
    observers.push_back(observer);
  }

  void removeObserver(shared_ptr<Observer> observer) override {
    observers.erase(
      remove_if(observers.begin(), observers.end(),
        [&](const weak_ptr<Observer>& weakObserver) {
          auto sharedObserver = weakObserver.lock();

          return !sharedObserver || sharedObserver == observer;
        }
      ),
      observers.end()
    );
  }

  void notifyObservers() override {
    for (auto it = observers.begin(); it != observers.end() ;) {
      auto observer = it->lock();

      if (observer) {
        observer->update(latestNews);
        ++it;
      } else {
        it = observers.erase(it);
      }
    }
  }
  // news를 발행할 때 구독중인 observer들에게 전부 알림 
  void publishNews(const string& news) {
    latestNews = news;
    notifyObservers();
  }
};

weak_ptr은 객체에 대한 약한 참조를 가진다. 따라서 이것이 사라져도 객체의 생명주기와는 관련이 없다. 즉 weak_ptr은 객체를 소유하지 않는다. 그러므로 remove 나 notify 시점에 실제 객체가 살아있는지 확인을 해야한다. lock 함수는 weak_ptr이 가르키던 객체가 아직 살아있다면 해당 객체를 가르키는 shared_ptr을 반환한다. 이미 객체가 사라졌다면 빈 shared_ptr을 반환한다.
erase 함수는 지정한 원소를 지운후 다음 위치를 가르키는 iterator를 반환한다.

class EmailSubscriber : public Observer {
private:
  string email;
public:
  EmailSubscriber(const string& email) : email(email) {}

  void update(const string& news) override {
    cout << "[Email] " << email << "에게 뉴스 전송: " << news << '\n';
  }
};


class AppSubscriber : public Observer {
private:
  string username;
public:
  AppSubscriber(const string& username) : username(username) {}

  void update(const string& news) override {
    cout << "[App] " << username << "에게 푸시 알림: " << news << '\n';
  }
};

각 구현체 별로 변화하는 update 함수만 따로 구현해주면 다른 구현체 클래스들도 손쉽게 추가할 수 있다. 이후에 addObserver와 removeObserver을 통해 유기적으로 등록 및 삭제를 할 수 있다.

int main (void) {
  NewsAgency agency;

  shared_ptr<Observer> emailUser = make_shared<EmailSubscriber>("leokang123@naver.com");

  shared_ptr<Observer> appUser = make_shared<AppSubscriber>("leokang123");

  agency.addObserver(emailUser);
  agency.addObserver(appUser);

  agency.publishNews("첫 번째 뉴스입니다.");

  cout << "---- 앱 구독자 해지 ----" << '\n';

  agency.removeObserver(appUser);

  agency.publishNews("두 번째 뉴스입니다.");

  return 0;
}



-------- 실행 결과 -------- 
[Email] leokang123@naver.com에게 뉴스 전송: 첫 번째 뉴스입니다.
[App] leokang123에게 푸시 알림: 첫 번째 뉴스입니다.
---- 앱 구독자 해지 ----
[Email] leokang123@naver.com에게 뉴스 전송: 두 번째 뉴스입니다.

번외

현재 방식은 subject 객체에서 observer들에게 push 방식으로 notify하는 방식이다.

이 방식을 pull 방식으로 구현할수도 있다.

subject에서 바뀐점이 있으면 구독중인 observer들에게 알림을 보내고, observer들이 직접 update함수를 호출하여 값을 갱신하는 방법이다.


Share this post on:

Previous Post
Chapter 1. Computer Networks and the Internet
Next Post
데코레이터 패턴