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

一、定义

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


二、要点

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

三、类图


四、代码

        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设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值