[Design Pattern] 관찰자 패턴
옵저버 패턴이란?
옵저버 패턴은 한 객체(주체, Subject)의 상태가 변경될 때, 그 객체에 의존하는 다른 객체들(관찰자, Observer)에게 자동으로 통지하도록 하는 디자인 패턴입니다. 주체는 관찰자 목록을 유지하고, 상태가 변경될 때마다 이 목록을 순회하며 각 관찰자에게 업데이트를 알립니다. 현업에서도 자주 쓰이기 때문에 익혀두시는것을 추천합니다.
옵저버 패턴의 구성 요소
Subject
(주체): 상태를 가지고 있으며, 관찰자 목록을 관리합니다. 상태가 변경될 때 관찰자들에게 통지하는 역할을 합니다.Observer
(관찰자): 주체의 상태 변화를 감지하고 업데이트를 받는 인터페이스를 정의합니다.ConcreteSubject
(구체적인 주체): Subject 인터페이스를 구현하며, 실제 상태를 가지고 있습니다.ConcreteObserver
(구체적인 관찰자): Observer 인터페이스를 구현하며, 주체의 업데이트에 대한 구체적인 동작을 정의합니다.
옵저버 패턴의 동작 방식
- 관찰자는 주체에 자신을 등록(구독)합니다.
- 주체의 상태가 변경됩니다.
- 주체는 등록된 모든 관찰자에게 업데이트를 통지합니다.
- 각 관찰자는 업데이트 통지를 받고 필요한 동작을 수행합니다.
옵저버 패턴의 장단점
장점
느슨한 결합(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;
}