C++ 設計模式 - 觀察者模式 Observer Pattern

今天你訂閱了一個喜歡的 YouTube 頻道,這個頻道一旦有新影片發布,你就會收到通知。不需要你反覆檢查頻道是否有新內容,所有更新自動發送到你手上。在軟體開發中,我們常常需要處理這樣的情境,當一個物件的狀態發生變化時,其他相關物件需要自動更新,並做出相應的回應。這種情境下我們就可以運用觀察者模式 Observer Pattern。

什麼是觀察者模式?

觀察者模式是一種行為設計模式,它定義了一種一對多的依賴關係,當一個物件(被觀察者)發生改變時,所有依賴它的物件(觀察者)都會自動收到通知並更新自己。這讓系統中的物件之間能夠鬆散耦合,保持靈活性和可維護性。

觀察者模式在 YouTube 訂閱的應用

在這個範例中,我們將 YouTube 頻道視為「被觀察者」,訂閱該頻道的使用者則是「觀察者」。當頻道有新影片上傳時,所有訂閱者都會自動收到通知。讓我們用 C++ 來實踐這個概念。

我們定義一個 Observer 介面,所有的觀察者(訂閱者)都要實作這個介面。

1
2
3
4
5
class Observer {
public:
virtual void update(const std::string& videoTitle) = 0;
virtual ~Observer() = default;
};

接著我們定義一個 Subject 介面,讓被觀察者(YouTube 頻道)管理觀察者的訂閱和通知。

1
2
3
4
5
6
7
class Subject {
public:
virtual void addObserver(std::shared_ptr<Observer> observer) = 0;
virtual void removeObserver(std::shared_ptr<Observer> observer) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() = default;
};

接下來我們實作一個具體的 YouTubeChannel 類別,這個類別代表 YouTube 頻道,當有新影片發布時,它會通知所有的訂閱者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class YouTubeChannel : public Subject {
private:
std::list<std::shared_ptr<Observer>> subscribers;
std::string latestVideo;

public:
void addObserver(std::shared_ptr<Observer> observer) override {
subscribers.push_back(observer);
}

void removeObserver(std::shared_ptr<Observer> observer) override {
subscribers.remove(observer);
}

void notifyObservers() override {
for (const auto& observer : subscribers) {
observer->update(latestVideo);
}
}

void uploadNewVideo(const std::string& videoTitle) {
latestVideo = videoTitle;
notifyObservers();
}
};

最後我們可以定義多個觀察者來代表不同的訂閱者,他們會在接收到新影片通知時,顯示影片的標題。

1
2
3
4
5
6
7
8
9
10
11
12
class Subscriber : public Observer {
private:
std::string name;

public:
Subscriber(const std::string& name) : name(name) {}

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

當我們將 Subscriber 加入 YouTubeChannel 的訂閱者清單時,頻道一旦上傳新影片,所有的訂閱者都會自動收到通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main() {
YouTubeChannel channel;

std::shared_ptr<Subscriber> alice = std::make_shared<Subscriber>("Alice");
std::shared_ptr<Subscriber> bob = std::make_shared<Subscriber>("Bob");

channel.addObserver(alice);
channel.addObserver(bob);
channel.uploadNewVideo("First Video");

channel.removeObserver(bob);
channel.uploadNewVideo("Second Video");

return 0;
}

執行上述程式碼,我們會得到以下輸出:

1
2
3
Alice received notification: First Video
Bob received notification: First Video
Alice received notification: Second Video

這個範例中使用了觀察者模式來模擬 YouTube 訂閱系統。當 YouTube 頻道上傳新影片時,所有的訂閱者都會自動收到通知。這讓我們的系統能夠更靈活地應對訂閱和通知的需求。

觀察者模式的優點

以這個例子來說,使用觀察者模式降低了耦合度,訂閱者和 YouTube 頻道之間的關係是鬆散的,YouTube 頻道不需要知道實際上是誰訂閱了它,它只需要通知所有訂閱者,這讓系統更容易擴展跟維護。

同時也具備擴展性,你可以輕鬆地新增或移除訂閱者,而不需要修改 YouTube 頻道的程式碼。

總結

觀察者模式是一種常見的設計模式,能有效處理多個物件之間的通知和更新需求。在我們生活中無所不在,例如:股票價格變化時,關注該股票的投資者都會收到通知,天氣變化時,訂閱天氣服務的使用者都會收到更新,電子報發布時,訂閱的讀者也會收到信件。下次你在設計系統時,如果遇到一對多的依賴關係,並且希望它們能夠自動更新的情況,不妨試試觀察者模式!