您的位置:首页 > 其它

Head-first设计模式(二)——观察者模式(Observer Pattern)

2012-05-06 17:16 489 查看

一、定义

观察者模式在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

二、要点

观察者模式定义了对象之间的一对多关系
主题(可观察者)用一个共同的接口来更新观察者
可观察者不知道观察者的细节,只知道观察者实现了观察者接口
使用此模式时,你可从被观察者处推(push)或者拉(pull)数据(然而,推的方式被认为更“正确”)
有多个观察者时,不可以依赖特定的通知次序

三、类图



四、代码

1. 采用“推(push)”的方式:

class Subject {
public:
Subject(){};
virtual ~Subject(){};

void registerObserver(Observer *o);
void removeObserver(Observer *o);
virtual void notifyObservers() = 0;

protected:
vector<Observer*> observers;
};

void Subject::registerObserver(Observer *o){
observers.push_back(o);
}

void Subject::removeObserver(Observer *o){
remove(observers.begin(), observers.end(), o);
}

class ConcreteSubject : public Subject {
public:
ConcreteSubject():changed(false),data1(0),data2(0.0),data3(""){};
~ConcreteSubject(){};

void notifyObservers();
bool isChanged();
void setChanged();

void changeData(int d1, float d2, string d3);
void dataChanged();
private:
bool changed;
int data1;
float data2;
string data3;
};

void ConcreteSubject::notifyObservers(){
if(isChanged())
{
for(vector<Observer*>::iterator it = observers.begin(); it != observers.end(); ++it){
it->update(data1, data2, data3);
}
changed = false;
}
}

void ConcreteSubject::changeData(int d1, float d2, string d3){
data1 = d1;
date2 = d2;
data3 = d3;

dataChanged();
}

void ConcreteSubject::dataChanged(){
setChanged();
notifyObservers();
}

void ConcreteSubject::setChanged(){
changed = true;
}

bool ConcreteSubject::isChanged(){
return changed;
}

class Observer {
public:
Observer(){}
~Observer(){}

virtual void update(int d1, float d2, string d3) = 0;
};

class ConcreteObserver : public Observer {
public:
ConcreteObserver(Subject *pSubject);
~ConcreteObserver(){}
void update(int d1, float d2, string d3);
void display();

void unregister();
private:
Subject *pSubject;    //为了以后取消注册方便,保留此引用

int data1;
float data2;
};

ConcreteObserver::ConcreteObserver(Subject *pSubject){
if(pSubject){
this.pSubject = pSubject;
this.pSubject.registerObserver(this);
}
}

void ConcreteObserver::unregister(){
if(pSubject){
this.pSubject.removeObserver(this);
}
}

void display(){
printf("data1:%d, data2:%f\n", data1, data2);
}

void update(int d1, float d2, string d3){
data1 = d1;
data2 = d2;
}


2. 采用“拉(pull)”的方式

class DataObj{
};

class ConcreteSubjectDataObj : public DataObj
{
public:
int data1;
float data2;
string data3;
};

class Subject {
public:
Subject():type("Subject"){};
virtual ~Subject(){};

void registerObserver(Observer *o);
void removeObserver(Observer *o);
virtual void notifyObservers() = 0;

void getType(){return type;}
protected:
vector<Observer*> observers;
string type;
};

void Subject::registerObserver(Observer *o){
observers.push_back(o);
}

void Subject::removeObserver(Observer *o){
remove(observers.begin(), observers.end(), o);
}

class ConcreteSubject : public Subject {
public:
ConcreteSubject():changed(false),type("ConcreteSubject"){
dataObj.data1 = 0;
dataObj.data2 = 0.0;
dataObj.data3 = "";
};
~ConcreteSubject(){};

void notifyObservers(int type = 0);	// 0 for push, 1 for pull
bool isChanged();
void setChanged();

void changeData(ConcreteSubjectDataObj *pData);
void dataChanged();

//a group of getter method
int getData1(){return pData->data1;}
float getData2(){return pData->data2;}
string getData3(){return pData->data3;}
private:
bool changed;
ConcreteSubjectDataObj dataObj;
};

void ConcreteSubject::notifyObservers(int type){
if(isChanged())
{
ConcreteSubjectDataObj *pDataObj = (0 == type) ? &dataObj : NULL;
for(vector<Observer*>::iterator it = observers.begin(); it != observers.end(); ++it){
it->update(this, pDataObj);
}
changed = false;
}
}

void ConcreteSubject::changeData(ConcreteSubjectDataObj *pData){
this.dataObj = *pData;

dataChanged();
}

void ConcreteSubject::dataChanged(){
setChanged();

/* pull */
notifyObservers(1);

/** push
notifyObservers(0);
*/
}

void ConcreteSubject::setChanged(){
changed = true;
}

bool ConcreteSubject::isChanged(){
return changed;
}

class Observer {
public:
Observer(){}
virtual ~Observer(){}

virtual void update(Subject *pSubject, DataObj *pDataObj) = 0;
};

class ConcreteObserver : public Observer {
public:
ConcreteObserver(Subject *pSubject);
~ConcreteObserver(){}
void update(Subject *pSubject, DataObj *pDataObj);
void display();

void unregister();
private:
Subject *pSubject;	//为了以后取消注册方便,保留此引用

int data1;
float data2;
};

ConcreteObserver::ConcreteObserver(Subject *pSubject){
if(pSubject){
this.pSubject = pSubject;
this.pSubject.registerObserver(this);
}
}

void ConcreteObserver::unregister(){
if(pSubject){
this.pSubject.removeObserver(this);
}
}

void display(){
printf("data1:%d, data2:%f\n", data1, data2);
}

void update(Subject *pSubject, DataObj *pDataObj){
if(!pSubject->getType().compare("ConcreteSubject")){
data1 = pSubject->getData1();
data2 = pSubject->getData2();
}
}


五、疑问解答

1. 关于采用推方式和拉方式

推方式:

优点:可以在一次通知中一口气得到所有东西;

缺点:有可能造成只需要一点点数据的类被强迫收到一堆数据;

拉方式:

优点:

需要什么数据就拉什么数据,不会有冗余数据;
当主题增加新状态时,只需要增加相应的getter方法,不需要修改和更新每位观察者的调用

缺点:

被观察者门户大开,不管是注册还是未注册的观察者都能获取被观察者的数据;
有可能需要调用很多次getter方法才能收集到需要的所有数据

六、参考文献

1. Head First设计模式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: