設(shè)計模式之Observer模式
引例:
銀行現(xiàn)在的業(yè)務(wù)大多有提醒業(yè)務(wù),比如我們用信用卡消費的時候銀行會有短信通知和Email通知等方法立即提醒客戶賬戶發(fā)生了變化。這就是典型的Observer模式,A(賬戶)發(fā)生變化之后通知B(手機)和C(Email),以后也許還會通知D(電話)等等。。。
程序設(shè)計中也會遇到很多這樣的問題,比如說MFC中的Document/View架構(gòu),Document中的數(shù)據(jù)變化會立即通知View的顯示也相應(yīng)變化,View的顯示變化后也會通知Document中的數(shù)據(jù)產(chǎn)生對應(yīng)變化;再比如回調(diào)函數(shù),觸發(fā)某個事件后通知調(diào)用回調(diào)函數(shù)。但凡“通知依賴”問題大都可以用到Observer模式。
動機:
為某些對象建立“通知依賴關(guān)系”——“一個對象的狀態(tài)發(fā)生改變,那么與之相關(guān)的所有對象都要改變”。
顯然這種關(guān)系是緊耦合的,為保證這種結(jié)構(gòu)在遇到變化時能夠比較穩(wěn)定,我們必須使之松耦合。
意圖:
定義對象間的一種一對多關(guān)系,以便當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都得到通知和自動更新(GoF23)
設(shè)計思路:
我們的目的是在A的狀態(tài)發(fā)生改變時會自動通知B,上下兩幅圖分別代表了兩個設(shè)計的方法。
上圖是一種較容易想到的解決方法,但是可以看出A依賴于B,兩者是緊耦合關(guān)系。
OO中有一個重要的原則就是“依賴倒置原則”,于是就有了下圖:
接口IA依賴于接口IB,而IA和IB是從A,B中抽象出來的相對穩(wěn)定的部分,IA和IB之間的依賴關(guān)系相對穩(wěn)定,不易變化。
Ok,就是這么簡單!Observer模式的思想就是這樣!問題的關(guān)鍵在于如何抽象IA和IB,下面是GoF23對Observer模式描述的UML圖
其中Subject即接口IA,Observer即接口IB。
Observer只有一個方法Update,用于更新具體的Observer。
Subject一般有一個List用于保存Observer的信息;Attach負責(zé)添加Observer;Detach負責(zé)刪除Observer;Notify通知List中的所有Observer調(diào)用Update。
關(guān)于內(nèi)存釋放:
需要注意的是在ISubject的析構(gòu)函數(shù)中清空List之前,必須首先釋放其中每個指針的內(nèi)存
自己測試用的代碼,僅供參考

/**///////////////////////////////////////////////////////////////////////////2
//ObserverTest For Observer Pattern3
//4

/**///////////////////////////////////////////////////////////////////////////5

6
#include "stdafx.h"7
#include "iostream"8
#include <string>9
#include <list>10
using namespace std;11

12
//賬戶參數(shù)13
struct AccountArgs14


{15
string email;16
string phoneNum;17
};18

19
//IObserver接口20
class IObserver21


{22
public:23
virtual void Update(AccountArgs args) = 0;24
};25

26
//具體類實現(xiàn)IObserver接口——Email27
class Emailer : public IObserver28


{29
public:30
void Update(AccountArgs args)31

{32
string toAddress = args.email;33
cout<<"Email 通知:"<<args.email<<endl;34
}35
};36

37
//具體類實現(xiàn)IObserver接口——Phone38
class Mobile : public IObserver39


{40
public:41
void Update(AccountArgs args)42

{43
string phoneNumber = args.phoneNum;44
cout<<"手機 通知:"<<args.phoneNum<<endl;45
}46
};47

48
//ISubject接口,抽象類49
class ISubject50


{51
protected:52
//"一對多關(guān)系",可能有多個觀察者53
list<IObserver*> observerList;54
protected:55
//notify,addObserver,removeObserver為穩(wěn)定的方法,提取出來56
virtual void notify(AccountArgs args) = 0;57

58
void addObserver(IObserver* observer)59

{60
observerList.push_back(observer);61
}62

63
bool removeObserver(IObserver* observer)64

{65
list<IObserver*>::iterator itr;66
for (itr = observerList.begin();itr!=observerList.end();itr++)67

{68
if (*itr == observer)69

{70
delete *itr;71
observerList.erase(itr);72
return true;73
}74
}75
return false;76
}77

78
//析構(gòu)函數(shù)79
~ISubject()80

{81
//首先釋放指針,之后清空List82
list<IObserver*>::iterator itr;83
for (itr = observerList.begin();itr!=observerList.end();itr++)84

{85

if ((*itr)!=NULL)
{ delete (*itr); }86
}87
observerList.clear();88
}89
};90

91
//賬戶類,實現(xiàn)ISubject接口功能92
class BankAccount: public ISubject93


{94
public:95
BankAccount()96

{97
addObserver(new Emailer());98
addObserver(new Mobile());99
}100

101
void notify(AccountArgs args)102

{103
for(list<IObserver*>::iterator itr = observerList.begin();itr!=observerList.end();itr++)104

{105
(*itr)->Update(args);106
}107
}108

109
//提款方法——通知Observer110
void withDraw()111

{112

/**///////////////////////////////////////////////////////////////////////////113
//提款操作
.114

/**///////////////////////////////////////////////////////////////////////////115

116
//Observer的一些參數(shù)117
AccountArgs args;118
args.email = "qudeqing@126.com";119
args.phoneNum = "1395517XXXX";120
notify(args); 121
}122

123
};124

125
int _tmain(int argc, _TCHAR* argv[])126


{127
BankAccount bankAccount;128
bankAccount.withDraw();129
return 0;130
}posted on 2009-04-05 22:51 Alex@VCC 閱讀(1766) 評論(3) 編輯 收藏 引用 所屬分類: 設(shè)計模式

