2 minute read

옵저버 패턴이란?


옵저버 패턴은 한 객체(주체, Subject)의 상태가 변경될 때, 그 객체에 의존하는 다른 객체들(관찰자, Observer)에게 자동으로 통지하도록 하는 디자인 패턴입니다. 주체는 관찰자 목록을 유지하고, 상태가 변경될 때마다 이 목록을 순회하며 각 관찰자에게 업데이트를 알립니다. 현업에서도 자주 쓰이기 때문에 익혀두시는것을 추천합니다.

옵저버 패턴의 구성 요소


  • Subject(주체): 상태를 가지고 있으며, 관찰자 목록을 관리합니다. 상태가 변경될 때 관찰자들에게 통지하는 역할을 합니다.
  • Observer(관찰자): 주체의 상태 변화를 감지하고 업데이트를 받는 인터페이스를 정의합니다.
  • ConcreteSubject(구체적인 주체): Subject 인터페이스를 구현하며, 실제 상태를 가지고 있습니다.
  • ConcreteObserver(구체적인 관찰자): Observer 인터페이스를 구현하며, 주체의 업데이트에 대한 구체적인 동작을 정의합니다.

옵저버 패턴의 동작 방식


  1. 관찰자는 주체에 자신을 등록(구독)합니다.
  2. 주체의 상태가 변경됩니다.
  3. 주체는 등록된 모든 관찰자에게 업데이트를 통지합니다.
  4. 각 관찰자는 업데이트 통지를 받고 필요한 동작을 수행합니다.

옵저버 패턴의 장단점


장점

  • 느슨한 결합(Loose Coupling): 주체와 관찰자는 서로에 대해 최소한의 정보만 알고 있습니다. 주체는 관찰자의 구체적인 클래스를 알 필요가 없습니다.
  • 개방-폐쇄 원칙(Open/Closed Principle) 준수: 새로운 관찰자를 추가하더라도 주체의 코드를 수정할 필요가 없습니다.
  • 유연성: 런타임에 동적으로 관찰자를 추가하거나 제거할 수 있습니다.

단점

  • 과도한 업데이트: 주체의 상태 변화가 너무 빈번하게 발생할 경우, 관찰자들에게 불필요한 업데이트가 전달될 수 있습니다.
  • 디버깅의 어려움: 업데이트 흐름이 명확하게 드러나지 않을 수 있어 디버깅이 어려울 수 있습니다.

활용 예시


  • GUI 프로그래밍: 버튼 클릭, 텍스트 변경 등의 이벤트 처리.
  • 모델-뷰-컨트롤러(MVC) 아키텍처: 모델의 변경을 뷰에 반영.
  • 주식 시세 알림: 주식 가격 변동 시 사용자에게 알림.

예제


#include <iostream>
#include <vector>
#include <string>

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

// Subject 인터페이스
class Subject {
public:
    virtual ~Subject() {}
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify() = 0;
};

// ConcreteSubject
class ConcreteSubject : public Subject {
private:
    std::vector<Observer*> observers;
    std::string message;

public:
    void attach(Observer* observer) override {
        observers.push_back(observer);
    }

    void detach(Observer* observer) override {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                return;
            }
        }
    }

    void notify() override {
        for (Observer* observer : observers) {
            observer->update(message);
        }
    }

    void setMessage(const std::string& msg) {
        message = msg;
        notify(); // 상태 변경 시 관찰자들에게 알림
    }
};

// ConcreteObserver
class ConcreteObserver : public Observer {
private:
    std::string name;

public:
    ConcreteObserver(const std::string& n) : name(n) {}

    void update(const std::string& message) override {
        std::cout << name << " received message: " << message << std::endl;
    }
};

int main() {
    ConcreteSubject subject;
    ConcreteObserver observer1("Observer 1");
    ConcreteObserver observer2("Observer 2");

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.setMessage("Hello, observers!");
    subject.setMessage("Another message!");

    subject.detach(&observer1);

    subject.setMessage("Only observer 2 will receive this.");

    return 0;
}


Top