天天看點

設計模式學習筆記--觀察者模式一.簡介二.觀察者模式的實作 三.觀察者模式的使用條件 四.觀察者模式和回調函數

一.簡介

觀察者模式是一種灰常灰常重要的設計模式!在我們程式設計的時候經常會用到觀察者模式,雖然我們自己可能并不知道。觀察者模式又叫做釋出-訂閱模式。定義了一種一對多的依賴關系,讓多個觀察者同時監聽同一個對象,該對象的狀态發生改變時,會通知所有觀察者對象,使他們更新自己。 舉個例子來說,比如我們在玩一個網絡遊戲,場景中出現了一個BOSS,那麼所有在這個場景中的人都應該能知道BOSS出現了,換句話說,伺服器端需要将BOSS出現的這個資訊發給所有在這個地圖上的玩家。而要實作通知,伺服器肯定不會去周遊所有在伺服器中的玩家找出在此場景的玩家,這樣做太耗費時間了,更好的做法是玩家進入了這個場景,就将自己“注冊”到這個場景的一個清單中。這樣當有消息需要釋出時,伺服器隻需要周遊這個清單,就可以通知所有在這個地圖上的玩家了。

觀察者模式的UML圖如下:

設計模式學習筆記--觀察者模式一.簡介二.觀察者模式的實作 三.觀察者模式的使用條件 四.觀察者模式和回調函數

二.觀察者模式的實作

關于觀察者模式的實作,看下面的一個例子:

// C++Test.cpp : 定義控制台應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;


//抽象觀察者類
class Observer
{
public:
	virtual void Update() = 0; 
};

//抽象主題類,被觀察的對象
class Subject
{
private:
	//觀察者清單
	typedef list<Observer*> ObserverList;
	ObserverList m_ObserverList;
public:
	//添加觀察者
	void AddObserver(Observer* observer);
	//删除觀察者
	void DelObserver(Observer* observer);

	//通知觀察者進行更新
	void Notify();
};

void Subject::AddObserver(Observer* observer)
{
	m_ObserverList.push_back(observer);
}

void Subject::DelObserver(Observer* observer)
{
	m_ObserverList.remove(observer);
}

void Subject::Notify()
{
	for(ObserverList::iterator it = m_ObserverList.begin();it != m_ObserverList.end(); ++it)
	{
		(*it)->Update();
	}
}

//具體觀察者類
class ConcreteObserver : public Observer
{
private:
	string m_name;
public:
	void SetName(string name);
	void Update() override;
};

void ConcreteObserver::SetName(string name)
{
	m_name = name;
}

void ConcreteObserver::Update()
{
	cout<<"Concrete Observer "<<m_name<<" update"<<endl;
}

//具體主題類,被觀察者
class ConcreteSubject : public Subject
{
	//此處未給出具體實作
};

int _tmain(int argc, _TCHAR* argv[])
{
	//建立具體主題類(被觀察者)
	ConcreteSubject* subject = new ConcreteSubject();
	//建立觀察者
	ConcreteObserver* observer1 = new ConcreteObserver();
	observer1->SetName("Observer1");
	ConcreteObserver* observer2 = new ConcreteObserver();
	observer2->SetName("Observer2");
	ConcreteObserver* observer3 = new ConcreteObserver();
	observer3->SetName("Observer3");

	//将觀察者注冊到主題對象中
	subject->AddObserver(observer1);
	subject->AddObserver(observer2);
	subject->AddObserver(observer3);

	//主題對象有操作了,通知具體對象
	subject->Notify();


	//取消觀察者3号的注冊
	cout<<endl;
	subject->DelObserver(observer3);
	subject->Notify();

	system("pause");
	return 0;
}
           

結果: Concrete Observer Observer1 update

Concrete Observer Observer2 update

Concrete Observer Observer3 update

Concrete Observer Observer1 update

Concrete Observer Observer2 update

請按任意鍵繼續. . .

例子中,我們将Observer注冊到了Subject中了,這樣,如果Subject有任何變化,都可以通過調用Notify方法,通過Subject來多态調用各個ConcreteObserver的Update方法。當然這個注冊也是提供登出操作的,我們将Observer3登出之後,再次Notify之後,他就沒有再收到消息了。

三.觀察者模式的使用條件

雖然這個是被觀察對象改變之後,通知所有觀察對象,但是可以繼續引申一下,比如給觀察者提供一個接口,讓其可以通過被觀察者的接口進行通知,或者直接将被觀察者置為單例模式,這樣就可以在一群對象中,一個對象的狀态改變,通知所有對象。 觀察者模式非常非常常用,在很多情況下我們都會用到它。著名的MVC架構就是基于觀察者模式的。它為實作對象之間的關聯提供了一整套解決方案,涉及到一對一或者一對多的對象互動時就可以考慮使用觀察者模式。

觀察者模式有如下優點: 1.可以在目标和觀察者之間建立抽象的耦合,觀察目标不需要了解觀察者内部構造,隻需要了解一個通知觀察者的接口即可。 2.觀察者模式可以實作表示和資料邏輯的分離,定義了穩定的消息傳遞機制,抽象了接口,讓不同的表示層充當具體的觀察者角色,觀察者隻需要繼承抽象接口即可。、 3.觀察者模式支援廣播通信,可以向多個目标發送消息。 4.新增觀察者無需修改原有系統代碼,符合“開放封閉原則”。

但是觀察者模式也有一些缺點: 1.雖然比暴力查詢模式效率高得多,但是如果觀察者較多的話,傳遞消息也是比較慢 2.如果觀察者和目标之間存在循環依賴,觀察者會觸發他們之間的循環調用,可能導緻崩潰。 3.沒有讓觀察者知道目标怎麼發生變化,隻能知道目标發生了變化。

四.觀察者模式和回調函數

關于觀察這模式與回調函數是什麼關系呢?個人感覺回調函數是一種觀察者模式的實作方式,如果在沒有對象概念的C中,要實作觀察者模式一般就要用回調函數來實作。函數回調的過程其實就是一個“釋出-訂閱”的過程。我們将函數賦給函數指針,就是注冊的過程,當狀态改變就可以通過函數指針來進行回調,讓觀察者行動。而在面向對象語言中,我們就可以用面向對象的思維來更進一步實作觀察者模式。我們注冊的内容不再僅僅是一個函數指針,而變成了一個對象的指針或者引用,這樣比單用函數指針可以實作的操作更多。 PS:關于函數指針,以及回調函數,可以參考: http://blog.csdn.net/puppet_master/article/details/49368863

繼續閱讀