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

/**///////////////////////////////////////////////////////////////////////////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
//具體類實(shí)現(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
//具體類實(shí)現(xiàn)IObserver接口——Phone38
class Mobile : public IObserver39


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

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

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


{51
protected:52
//"一對(duì)多關(guān)系",可能有多個(gè)觀察者53
list<IObserver*> observerList;54
protected:55
//notify,addObserver,removeObserver為穩(wěn)定的方法,提取出來(lái)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
//賬戶類,實(shí)現(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) 評(píng)論(3) 編輯 收藏 引用 所屬分類: 設(shè)計(jì)模式

