Observer là một Pattern được sử dụng cực kì rộng rãi, đặc biệt là trong các bài toán event-handling, event-driven software. Nó còn được biết đến như Publish-Subscribe hay Event-Listener Pattern.
Đặt vấn đề
Hãy thử tưởng tượng bạn có một cửa hàng bán đồ ăn online, khi có rất nhiều khách hàng quan tâm tới cửa hàng của bạn nhưng họ không có thời gian để vào ứng dụng kiểm tra các dịch vụ mới của bạn như: có thực đơn mới, món ăn mới, mã giảm giá, ….
Để giúp khác hàng cập nhật thông báo một các realtime bạn cần có một hệ thống cho phép khách đăng kí các dịch vụ muốn nhận thống báo từ cửa hàng, sau đó mỗi khi cửa hàng có cập nhập mới, khách hàng nào đăng kí dịch vụ sẽ tự động được gửi thông báo như sau:

Giải pháp
Để giải quyết bài toán trên, Observer là một giải pháp hoàn hảo nhất
Định nghĩa: “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updatedautomatically.” GoF
Hiểu đơn giản thì Observer có 2 thực thể là:
- Publisher: Giống như cửa hàng của chúng ta sẽ có nhiệm vụ cho phép đăng kí hoặc bỏ đăng đăng kí thêm các thành viên mới, sau đó gửi thông báo tới các thành viên đăng kí mỗi khi có cập nhật (Publisher chỉ có một).
- Subscribers (Observers, Receivers): Tồn tại rất nhiều thành viên, họ có thể đăng kí với Publisher bất kì dịch vụ nào mà họ muốn, sau đó mỗi khi có cập nhật họ tự động nhận được thông báo từ các dụng vụ đăng kí.
Cơ chế hoạt động của Observer như sau:
- Bước 1: Các thành viên đăng kí với Publisher để trở thành Subscribers

- Bước 2: Khi có cập nhật thì những đối tượng đăng kí là Subscribers sẽ nhận được thông báo, còn lại thì không

- Bước 3: Nếu đối tượng nào không muốn nhận thông báo thì có thể Unregister

- Bước 4: Khi đó các thông báo mới sẽ không được đẩy tới các đối tượng đã Unregistered nữa

Implememt trong C++
- Chúng ta sẽ thiết kế một hệ thống cửa hàng máy tính (ComputerShop) là Publisher, các khách hàng là các Subscribers như sau:

- Tạo Publisher có các chức năng cho phép Register, Unregister, Notify
class Publisher
{
private:
/* data */
std::vector<Observer*> m_observers;
public:
Publisher(/* args */) { }
virtual ~Publisher(){}
void subscribe(Observer* observer)
{
m_observers.push_back(observer);
}
void unsubscribe(Observer* observer)
{
auto ob = std::find_if(m_observers.begin(), m_observers.end(),
[&](Observer* obj){ return (observer == obj);});
if(ob != m_observers.end())
{
size_t i = std::distance(m_observers.begin(), ob);
m_observers.erase(m_observers.begin() + i);
}
}
void nofify()
{
for(auto& observer : m_observers){
std::cout << "---------------------------------" << std::endl;
std::cout << "Dear " << observer->get_name() << ", "<< std::endl;
observer->update();
}
}
size_t getTotalSubscriber()
{
return m_observers.size();
}
virtual void setDiscount(std::string serial, float discount, int numberOfProduct) = 0;
virtual void getDiscount() = 0;
};
class ComputerShop : public Publisher
{
private:
float m_discount = 0.0;
std::string m_serial;
int m_numberOfProduct;
public:
ComputerShop(){}
~ComputerShop() override
{
}
void setDiscount(std::string serial, float discount, int numberOfProduct) override
{
m_discount = discount;
m_serial = serial;
m_numberOfProduct = numberOfProduct;
}
void getDiscount() override
{
std::cout << "Discount Computer Shop notifications: " << std::endl;
std::cout << "serial: " << m_serial << std::endl;
std::cout << "discout: " << m_discount << "%" << std::endl;
std::cout << "available products: " << m_numberOfProduct << std::endl;
}
};
- Tạo Interface Observer, sau đó implement các khách hàng là các Subscribers
class Observer
{
public:
Observer() { }
virtual ~Observer(){}
virtual void update() = 0;
virtual std::string get_name() = 0;
};
class Subscriber1 : public Observer
{
private:
std::shared_ptr<Publisher> m_publiser = nullptr;
public:
Subscriber1(std::shared_ptr<Publisher> subject) : m_publiser(subject)
{
m_publiser->subscribe(this);
}
~Subscriber1() override
{
}
std::string get_name() override{
return "Subscriber 1";
}
void update() override
{
m_publiser->getDiscount();
}
};
class Subscriber2 : public Observer
{
private:
std::shared_ptr<Publisher> m_publiser = nullptr;
public:
Subscriber2(std::shared_ptr<Publisher> subject) : m_publiser(subject)
{
m_publiser->subscribe(this);
}
~Subscriber2() override
{
}
std::string get_name() override{
return "Subscriber 2";
}
void update() override
{
m_publiser->getDiscount();
}
};
class Subscriber3 : public Observer
{
private:
std::shared_ptr<Publisher> m_publiser = nullptr;
public:
Subscriber3(std::shared_ptr<Publisher> subject) : m_publiser(subject)
{
m_publiser->subscribe(this);
}
~Subscriber3() override
{
}
std::string get_name() override{
return "Subscriber 3";
}
void update() override
{
m_publiser->getDiscount();
}
};
- Tạo chương trình đăng kí Subscribers đến Publisher
int main()
{
std::cout << "Observer Pattern Design" << std::endl;
std::shared_ptr<Publisher> publisher = std::make_shared<ComputerShop>();
Observer* ob1 = new Subscriber1(publisher);
Observer* ob2 = new Subscriber2(publisher);
Observer* ob3 = new Subscriber3(publisher);
std::cout << "Total subscriber: "
<< publisher->getTotalSubscriber() << std::endl;
publisher->setDiscount("Dell", 20.0, 100);
publisher->nofify();
delete ob1;
delete ob2;
delete ob3;
return 0;
}
Chạy thử và xem kết quả

Như các bạn thấy, khi Publisher muốn gửi thông báo, nó sẽ gửi đến tất cả các Subscribers mà không cần quan tâm đến chúng là ai, các subscribers tự động nhận được thông báo mà không cần làm gì.
Ứng dụng
Observer Pattern được sử dụng hầu hết trong các hệ thống event-handling, event-driven và ứng dụng:
- Khi thay đổi một đối tượng đòi hỏi phải thay đổi đối tượng khác mà bạn không biết có bao nhiêu đối tượng cần phải thay đổi mà cũng không cần quan tâm đến các đối tượng đó là ai.
- Không chỉnh sửa, phá vỡ các đối tượng được thông báo, cũng không liên kết rằng buộc giữa các Subscribers
Kết luận
Như vậy trong bài viết này mình đã giới thiệu về Observer Pattern, cách sử dụng, và ứng dụng của nó.
Hi vọng bài viết hữu ích cho các bạn, nếu các bạn thấy bài viết hay thì hãy chia sẻ cho các anh em lập trình khác cùng biết nhé. Cám ơn các bạn đã ghé đọc ^^. Chúc các bạn làm việc hiệu quả ❤
