디자인 패턴 총정리 시리즈의 열세 번째 패턴은 Observer Pattern(옵저버 패턴)입니다. 객체 지향 프로그래밍에서 널리 활용되는 디자인 패턴 중 하나로, 소프트웨어 구조를 보다 유연하게 유지할 수 있도록 돕습니다.
Observer 패턴은 객체 간의 일대다 종속성을 정의하여 한 객체의 상태 변화가 다른 객체들에게 자동으로 통보되고 업데이트되도록 하는 강력한 디자인 패턴입니다. 특히 다양한 이벤트 처리나 알림 시스템에서 자주 사용됩니다.
이번 게시물에서는 Observer 패턴의 개념, 사용 예시, 그리고 장단점에 대해 자세히 알아보며, 효율적인 객체 간 통신 구조를 살펴보겠습니다.
1. 개요
Observer 패턴은 행동 패턴 중 하나로, 객체들 간의 일대다 의존 관계를 설정하여 한 객체(Subject)의 상태 변화가 다른 관련 객체들(Observers)에게 자동으로 통보되고 업데이트되도록 하는 구조입니다.
주로 이벤트 핸들링 시스템, 데이터 변경 알림 시스템 등에서 활용되며, 관찰자(Observer)와 피사체(Subject) 간의 느슨한 결합(loose coupling)을 통해 유연하고 확장 가능한 소프트웨어 아키텍처를 구현할 수 있습니다.
Observer 패턴은 Subject, Observer, ConcreteSubject, ConcreteObserver의 네 가지 주요 컴포넌트로 구성되며, 각각의 역할이 명확합니다.
2. 예시
다음은 Observer 패턴을 활용한 뉴스 알림 시스템의 시나리오를 예로 들어 보겠습니다.
뉴스 피드(Subject)는 새로운 뉴스가 업데이트될 때마다 등록된 모든 구독자(Observers)에게 이를 알립니다. 구독자들은 자신이 관심 있는 카테고리에 따라 관련 알림을 선택적으로 받습니다.
아래는 Observer 패턴을 사용한 간단한 C++ 코드 예시입니다.
#include
#include
#include
#include
// Observer interface
class Observer {
public:
virtual void update(const std::string& message) = 0;
};
// Subject interface
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
// Concrete Subject
class NewsFeed : public Subject {
private:
std::vector<Observer*> observers;
std::string latestNews;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify() override {
for (auto observer : observers) {
observer->update(latestNews);
}
}
void addNews(const std::string& news) {
latestNews = news;
notify();
}
};
// Concrete Observer
class Subscriber : public Observer {
private:
std::string name;
public:
Subscriber(const std::string& name) : name(name) {}
void update(const std::string& message) override {
std::cout << name << " received news: " << message << std::endl;
}
};
// Client
int main() {
NewsFeed newsFeed;
Subscriber subscriber1("Alice");
Subscriber subscriber2("Bob");
Subscriber subscriber3("Charlie");
newsFeed.attach(&subscriber1);
newsFeed.attach(&subscriber2);
newsFeed.addNews("New Observer Pattern Tutorial Released!");
newsFeed.attach(&subscriber3);
newsFeed.addNews("Observer Pattern Implemented in C++!");
newsFeed.detach(&subscriber2);
newsFeed.addNews("Latest Updates on Design Patterns!");
return 0;
}
이 예시에서 NewsFeed 클래스는 Subject 역할을 하며, 새로운 뉴스가 추가될 때마다 등록된 구독자들에게 알림을 전달합니다. Subscriber 클래스는 Observer 역할을 수행하며, 전달받은 뉴스를 출력하는 식으로 처리합니다.
이 구조를 통해 NewsFeed는 여러 구독자에게 유연하게 알림을 전송할 수 있으며, 구독자를 추가하거나 제거하는 것도 쉽게 관리할 수 있습니다.
3. 장점
- 객체 간의 일대일·일대다 종속 관계를 직관적으로 설정할 수 있습니다.
: Subject와 Observer 간의 명확한 의존 관계가 형성되어 관리가 용이합니다. - 관찰자와 피사체 사이의 loose coupling을 활성화합니다.
: Subject와 Observer는 서로 구체적인 구현을 모른 채 인터페이스로 소통하므로, 구조가 유연하며 유지보수가 쉽습니다.
4. 단점
- 대규모 시스템에서 메모리 누수 및 성능 문제가 발생할 수 있습니다.
: 여러 Observer가 동시에 등록되는 상황에서 관리가 복잡해지고, 적절한 해제가 이루어지지 않으면 메모리 누수가 생길 수 있습니다. - 복잡한 업데이트 흐름을 초래할 수 있습니다.
: Observer 간의 의존 관계가 많아질 경우, 업데이트 순서나 로직이 얽혀서 디버깅이 어려워질 수 있습니다.
5. 결론
Observer 패턴은 객체 간의 느슨한 결합을 유지하면서도 상태 변화를 효율적으로 통보할 수 있어, 이벤트 핸들링이나 알림 시스템 등에 적합한 디자인 패턴입니다. 그러나 대규모 시스템에 적용할 때는 메모리 관리와 업데이트 흐름의 복잡성을 주의 깊게 고려해야 합니다. 특히, 많은 관찰자가 등록될 경우 성능 최적화와 메모리 누수를 방지하기 위한 관리가 필수적입니다. 이와 같은 점들을 충분히 고려해 Observer 패턴을 적절히 도입한다면, 유연하고 확장 가능한 소프트웨어 아키텍처를 구축할 수 있습니다.
'Study > Software Architecture' 카테고리의 다른 글
[디자인패턴] 행동패턴(11) - Chain of Responsibility Pattern (책임 연쇄 패턴) (0) | 2023.06.12 |
---|---|
[디자인패턴] 행동패턴(9) - Memento Pattern (메멘토 패턴) (0) | 2023.06.12 |
[디자인패턴] 구조패턴(7) - Flyweight Pattern (플라이웨이트 패턴) (0) | 2023.06.06 |
[디자인패턴] 구조패턴(6) - Facede Pattern (파사드 패턴) (0) | 2023.06.05 |
[디자인패턴] 구조패턴(5) - Decorator Pattern (데코레이터 패턴) (0) | 2023.05.26 |