設計模式之觀察者模式
一、什么是觀察者模式
Observer模式也叫觀察者模式,是由GoF提出的23種軟件設計模式的一種。Observer模式是行為模式之一,它的作用是當一個對象的狀態發生變化時,能夠自動通知其他關聯對象,自動刷新對象狀態。
觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。舉個例子,用戶界面可以作為一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化后,就顯示在界面上。面向對象設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個對象只做一件事情,并且將他做好。觀察者模式在模塊之間劃定了清晰的界限,提高了應用程序的可維護性和重用性。
觀察者模式有很多實現方式,從根本上說,該模式必須包含兩個角色:觀察者和被觀察對象。在剛才的例子中,業務數據是被觀察對象,用戶界面是觀察者。觀察者和被觀察者之間存在“觀察”的邏輯關聯,當被觀察者發生改變的時候,觀察者就會觀察到這樣的變化,并且做出相應的響應。如果在用戶界面、業務數據之間使用這樣的觀察過程,可以確保界面和數據之間劃清界限,假定應用程序的需求發生變化,需要修改界面的表現,只需要重新構建一個用戶界面,業務數據不需要發生變化。
“觀察”不是“直接調用”
實現觀察者模式的時候要注意,觀察者和被觀察對象之間的互動關系不能體現成類之間的直接調用,否則就將使觀察者和被觀察對象之間緊密的耦合起來,從根本上違反面向對象的設計的原則。無論是觀察者“觀察”觀察對象,還是被觀察者將自己的改變“通知”觀察者,都不應該直接調用。
實現觀察者模式的形式
實現觀察者模式有很多形式,比較直觀的一種是使用一種“注冊——通知——撤銷注冊”的形式。
實現觀察者模式例子
下面是C++的實現,在C++實現中,C++中沒有接口的概念,但是可以用抽象類類代替Java或C#中的接口,在C++中抽象類中從派生類中抽象出來的函數(方法),必須定義成純虛函數,這樣在后面的使用中才可以通過基類的指針來訪問這些函數,面向對象的語言中有個特點,多態只能訪問兩者中共有的部分。

2
#include "stdafx.h"3
#include <string>4
#include <vector>5
#include<list>6
#include<iostream>7
using namespace std;8

9
class Observer;10
class Subject11
{12
public:13
//注冊14
virtual void attach(Observer *o)=0;15
//撤銷16
virtual void dettach(Observer *o)=0;17
//發送消息,更新觀察者數據18
virtual void change()=0;19
//更新數據20
virtual void setWeather(string str)=0;21
//獲取數據22
virtual string getWeather()=0;23
};24

25
class Observer26
{27
public:28
//獲得觀察者的名字,用于后面dettach撤銷操作29
virtual string getName()=0;30
//更新數據,用于被觀察者調用31
virtual void update(Subject *s)=0;32
};33

34
//被觀察者35
class Earth: public Subject36
{37
private:38
//數據39
string weather;40
//存放觀察者的list,因為Observer是個抽象類,41
//所以不能聲明成 list<Observer >* l42
list<Observer* >* l;//指針 43
public:44
//初始化存放觀察者的容器45
Earth()46
{47
l = new list<Observer*>;48
};49
//別忘了釋放資源50
~Earth()51
{52
delete l;53
};54
//注冊觀察者55
void attach(Observer *o)56
{57
this->l->push_back(o);58
};59
60
//注銷觀察者61
void dettach(Observer *o)62
{63
for(list<Observer*>::iterator it=l->begin();it!=l->end();++it)64
{65
//通過string來進行查找,其他通過指針怎么判斷相等比較困難66
if( (*it)->getName() == o->getName())67
{68
l->remove(*it);69
break;70
}71
}72
};73

74
//通知并更新觀察者75
void change()76
{77
for(list<Observer*>::iterator it=l->begin();it!=l->end();++it)78
{79
(*it)->update(this);80
}81
};82

83
//更新數據84
void setWeather(string str)85
{86
this->weather=str;87
change();88
};89

90
string getWeather()91
{92
return this->weather;93
};94

95
};96

97
class Satellite:public Observer98
{99
private:100
string name;101
public:102
Satellite(string str)103
{104
name=str;105
}106
string getName()107
{108
return name;109
};110
//更新數據111
void update(Subject *s)112
{113
cout<<this->getName()+" "+s->getWeather()<<endl;114
}115
};116

117

118

119
int _tmain(int argc, _TCHAR* argv[])120
{121
Earth e;122
Satellite *s1 = new Satellite("風云一號");123
Satellite *s2 = new Satellite("風云二號");124
Satellite *s3 = new Satellite("風云三號");125
Satellite *s4 = new Satellite("風云四號");126
e.attach(s1);127
e.attach(s2);128
e.attach(s3);129
e.attach(s4);130
e.setWeather("fine");131
e.setWeather("rain");132
//注銷s3133
e.dettach(s3);134
e.setWeather("fine");135
e.setWeather("rain");136

137

138
return 0;139
}140

141

以上文字參照http://blog.csdn.net/preciousboy/article/details/6230467,對其代碼進行修改在vs2008中編譯調試通過。
結果運行為:
這里要注意的幾個問題是?
1.被觀察者怎么去通知觀察者數據已經更新?--本例子中是通過void change()函數來實現的;
2.在此模式中你可以從被觀察者處push或者pull數據,我們認為push的方式是正確的。
3.觀察者模式定義了一對多的關系
4.有多個觀察者時不依賴于特定的通知次序。
5.java的MVC模式就采用此模式實現。



