의도
옵서버 패턴은 당신이 여러 객체에 자신이 관찰 중인 객체에 발생하는 모든 이벤트에 대하여 알리는 구독 메커니즘을 정의할 수 있도록 하는 행동 디자인 패턴입니다.

문제
Customer(손님) 및 Store(가게)라는 두 가지 유형의 객체들이 있다고 가정합니다. 손님은 곧 매장에 출시될 특정 브랜드의 제품(예: 새 아이폰 모델)에 매우 관심이 있습니다.
손님은 매일 매장을 방문하여 제품 재고를 확인할 수 있으나, 제품이 매장에 아직 운송되는 동안 이러한 방문 대부분은 무의미합니다.

매장 방문 vs. 스팸 발송
반면 매장에서는 새로운 제품이 출시될 때마다 모든 고객에게 스팸으로 간주할 수 있는 수많은 이메일을 보낼 수 있습니다. 이 수많은 이메일은 일부 고객들을 신제품 출시 확인을 위한 잦은 매장 방문으로부터 구출해낼 수 있으나, 동시에 신제품 출시에 관심이 없는 다른 고객들을 화나게 할 것입니다.
여기서 충돌이 발생합니다. 손님들이 신제품 출시 확인을 위해 시간을 낭비하든지, 매장들이 알림을 원하지 않는 고객들에게 신제품 출시를 알리며 자원을 낭비해야 합니다.
해결책
시간이 지나면 변경될 수 있는 중요한 상태를 가진 객체가 있다고 가정해봅시다. 이 객체는 종종 주제(subject)라고 불립니다. 그러나 위 예시의 경우 이 객체는 자신의 상태에 대한 변경에 대해 다른 객체들에 알림을 보내는 역할도 맡을 것이니 해당 객체를 출판사라고 부르겠습니다.
옵서버 패턴은 출판사 클래스에 개별 객체들이 그 출판사로부터 오는 이벤트들의 알림들을 구독 또는 구독 취소할 수 있도록 구독 메커니즘을 추가할 것을 제안합니다. 두려워하지 마세요. 그리 복잡하지 않습니다. 실제로 이 메커니즘은 1) 구독자 객체들에 대한 참조의 리스트를 저장하기 위한 배열 필드와 2) 그 리스트에 구독자들을 추가하거나 제거할 수 있도록 하는 여러 공개된(public) 메서드들로 구성됩니다.

구독 메커니즘을 통해 개별 객체들이 이벤트 알림들을 구독할 수 있습니다.
이제 출판사에 중요한 이벤트가 발생할 때마다 구독자 리스트를 참조한 후 그들의 객체들에 있는 특정 알림 메서드를 호출합니다.
실제 앱에는 같은 출판사 클래스의 이벤트들을 추적하는 데 관심이 있는 수십 개의 서로 다른 구독자 클래스들이 있을 수 있습니다. 당신은 출판사를 이러한 모든 클래스에 결합하고 싶지 않을 것입니다. 게다가 당신은 당신의 출판사 클래스가 다른 사람들에 의해 사용되어야 한다면 이러한 구독자 클래스 중 일부는 미리 알지 못할 수도 있습니다.
그러므로 모든 구독자가 같은 인터페이스를 구현하고 출판사가 오직 그 인터페이스를 통해서만 구독자들과 통신하는 것이 매우 중요합니다. 이 인터페이스는 출판사가 알림과 어떤 콘텍스트 데이터를 전달하는 데 사용할 수 있는 매개변수들의 집합과 알림 메서드를 선언해야 합니다.

출판사는 특정 알림 메서드를 구독자들의 객체들에서부터 호출하여 그들에게 알림을 보냅니다.
당신의 앱에 여러 유형의 출판사가 있고 이들을 구독자들과 호환되도록 하려면 당신은 더 나아가 모든 출판사가 같은 인터페이스를 따르도록 할 수 있습니다. 이 인터페이스는 몇 가지 구독 메서드들만 설명하면 됩니다. 이 인터페이스를 통해 구독자들은 출판자들의 상태들을 그들의 구상 클래스들과 결합하지 않고 관찰할 수 있습니다.
실제상황 적용

잡지 및 신문 구독.
당신이 신문이나 잡지를 구독한다면 다음 호가 있는지 확인하러 가게에 갈 필요가 없습니다. 대신 출판사가 발행 직후나 사전에 새 발행물을 구독자의 우편함으로 직접 보냅니다.
출판사는 구독자 리스트를 유지 관리하고 구독자들이 어떤 잡지에 관심 있는지 알고 있습니다. 출판사가 새로운 잡지의 발행호들를 보내는 것을 중단시키고 싶다면 구독자들은 언제든지 이 리스트를 떠날 수 있습니다.
의사코드
이 예시에서 옵서버 패턴은 텍스트 편집기 객체가 다른 서비스 객체들에 자신의 상태 변경에 대해 알릴 수 있도록 합니다.

다른 객체들에 발생하는 이벤트에 대해 객체들에 알립니다.
구독자 리스트는 동적으로 컴파일됩니다. 당신이 앱이 원하는 행동에 따라 객체들은 런타임 때 알림들을 받는 것을 시작하거나 중단할 수 있습니다.
이 구현에서 편집기 클래스는 자체적으로 구독 리스트를 유지 관리하지 않습니다. 편집기 클래스는 이 작업을 해당 작업을 전담하는 특수 도우미 객체에 위임합니다. 이 객체를 중앙 집중식 이벤트 디스패처 역할을 하도록 업그레이드하여 모든 객체가 출판사 역할을 하도록 할 수 있습니다.
앱에 새 구독자들을 추가할 때 기존 출판사 클래스들이 같은 인터페이스를 통해 모든 구독자와 작업하는 한 기존 출판사 클래스들은 변경할 필요가 없습니다.
/**
* Observer Design Pattern
*
* Intent: Lets you define a subscription mechanism to notify multiple objects
* about any events that happen to the object they're observing.
*
* Note that there's a lot of different terms with similar meaning associated
* with this pattern. Just remember that the Subject is also called the
* Publisher and the Observer is often called the Subscriber and vice versa.
* Also the verbs "observe", "listen" or "track" usually mean the same thing.
*/
#include <iostream>
#include <list>
#include <string>
class IObserver {
public:
virtual ~IObserver(){};
virtual void Update(const std::string &message_from_subject) = 0;
};
class ISubject {
public:
virtual ~ISubject(){};
virtual void Attach(IObserver *observer) = 0;
virtual void Detach(IObserver *observer) = 0;
virtual void Notify() = 0;
};
/**
* The Subject owns some important state and notifies observers when the state
* changes.
*/
class Subject : public ISubject {
public:
virtual ~Subject() {
std::cout << "Goodbye, I was the Subject.\n";
}
/**
* The subscription management methods.
*/
void Attach(IObserver *observer) override {
list_observer_.push_back(observer);
}
void Detach(IObserver *observer) override {
list_observer_.remove(observer);
}
void Notify() override {
std::list<IObserver *>::iterator iterator = list_observer_.begin();
HowManyObserver();
while (iterator != list_observer_.end()) {
(*iterator)->Update(message_);
++iterator;
}
}
void CreateMessage(std::string message = "Empty") {
this->message_ = message;
Notify();
}
void HowManyObserver() {
std::cout << "There are " << list_observer_.size() << " observers in the list.\n";
}
/**
* Usually, the subscription logic is only a fraction of what a Subject can
* really do. Subjects commonly hold some important business logic, that
* triggers a notification method whenever something important is about to
* happen (or after it).
*/
void SomeBusinessLogic() {
this->message_ = "change message message";
Notify();
std::cout << "I'm about to do some thing important\n";
}
private:
std::list<IObserver *> list_observer_;
std::string message_;
};
class Observer : public IObserver {
public:
Observer(Subject &subject) : subject_(subject) {
this->subject_.Attach(this);
std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\".\n";
this->number_ = Observer::static_number_;
}
virtual ~Observer() {
std::cout << "Goodbye, I was the Observer \"" << this->number_ << "\".\n";
}
void Update(const std::string &message_from_subject) override {
message_from_subject_ = message_from_subject;
PrintInfo();
}
void RemoveMeFromTheList() {
subject_.Detach(this);
std::cout << "Observer \"" << number_ << "\" removed from the list.\n";
}
void PrintInfo() {
std::cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << "\n";
}
private:
std::string message_from_subject_;
Subject &subject_;
static int static_number_;
int number_;
};
int Observer::static_number_ = 0;
void ClientCode() {
Subject *subject = new Subject;
Observer *observer1 = new Observer(*subject);
Observer *observer2 = new Observer(*subject);
Observer *observer3 = new Observer(*subject);
Observer *observer4;
Observer *observer5;
subject->CreateMessage("Hello World! :D");
observer3->RemoveMeFromTheList();
subject->CreateMessage("The weather is hot today! :p");
observer4 = new Observer(*subject);
observer2->RemoveMeFromTheList();
observer5 = new Observer(*subject);
subject->CreateMessage("My new car is great! ;)");
observer5->RemoveMeFromTheList();
observer4->RemoveMeFromTheList();
observer1->RemoveMeFromTheList();
delete observer5;
delete observer4;
delete observer3;
delete observer2;
delete observer1;
delete subject;
}
int main() {
ClientCode();
return 0;
}
'디자인 패턴' 카테고리의 다른 글
의존성 주입 (0) | 2025.04.08 |
---|---|
Memento 패턴 (3) | 2024.09.05 |
Mediator 패턴 (0) | 2024.09.05 |
iterator 패턴 (1) | 2024.09.05 |
Command 패턴 (0) | 2024.09.04 |