zoukankan      html  css  js  c++  java
  • 【设计模式】设计模式C++编程实现之观察者模式(ObserverPattern)

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

    观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。


    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己

    类图

    图一 观察者模式类图

    可以看出,在这个观察者模式的实现里有下面这些角色:

    1. 抽象主题(Subject)角色:主题角色把所有对观察考对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
    2. 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。
    3. 具体主题(ConcreteSubject)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。
    4. 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

    观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
    当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。

    关于观察者的一切,主题只知道观察者实现了某个接口(Observer)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
    任何时候我们可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表。在有新的类型的观察者出现时,主题的代码不需要修改。所要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。
    改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的。

    气象站实例C++编程实现 (来源于《Head First设计模式》):
    其类图:

    代码:
    //观察者模式C++编程实现
    //author:江南烟雨
    //E-Mail:xiajunhust@gmail.com
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    static int setConditionTimes = 0;//记录设置的总次数
    
    //观察者(布告板)抽象接口
    class Observer{
    public:
    	virtual void update(float temp,float humidity,float pressure) = 0;
    };
    
    //主题抽象接口
    class Subject{
    public:
    	virtual void registerObserver(Observer *o) = 0;//注册观察者
    	virtual void removeObserver(Observer *o) = 0;//删除观察者
    	virtual void notifyObserver() = 0;//当主题状态改变时,通知所有观察者
    };
    
    //布告板显示
    class DisplayElement{
    public: 
    	virtual void display()= 0;
    };
    
    //WeatherData具体类,继承自主题抽象类
    class WeatherData:public Subject{
    private:
    	vector<Observer *> observers;
    	float temperature;
    	float humility;
    	float pressure;
    
    public:
    	WeatherData(){}
    
    	~WeatherData(){
    		vector<Observer*>::iterator pos = observers.begin();
    		for (;pos != observers.end();++pos)
    		{
    			delete (*pos);
    		}
    	}
    
    	void registerObserver(Observer *o){
    		observers.push_back(o);
    	}
    
    	void removeObserver(Observer *o){
    		vector<Observer*>::iterator pos = find(observers.begin(),
    			observers.end(),o);
    		if (pos != observers.end())
    		{
    			observers.erase(pos);
    		}
    	}
    
    	void notifyObserver()
    	{
    		vector<Observer*>::iterator pos = observers.begin();
    		for (;pos != observers.end();++pos)
    		{
    			(*pos)->update(temperature,humility,pressure);
    		}
    	}
    
    	//当从气象站得到更新观测值时,通知观察者
    	void messurementsChanged(){
    		notifyObserver();
    	}
    
    	//设置气象站观测值
    	void setMessurements(float temperature,float humility,float pressure){
    		++setConditionTimes;
    		this->temperature = temperature;
    		this->humility = humility;
    		this->pressure = pressure;
    		messurementsChanged();
    	}
    };
    
    //布告板具体类:目前状况
    class CurrentConditionDisplay:public Observer,public DisplayElement{
    private:
    	float temperature;
    	float humility;
    	
    	Subject *weatherData;
    
    public: 
    	CurrentConditionDisplay(Subject *weatherData){
    		this->weatherData = weatherData;
    		weatherData->registerObserver(this);
    	}
    
    	~CurrentConditionDisplay(){}
    
    	void update(float temp,float humi,float pressure){
    		this->temperature = temp;
    		this->humility = humi;
    
    		display();
    	}
    
    	void display(){
    		cout << "Current conditions: " << temperature << "F degrees and " <<
    			humility << "% humility" << endl;
    	}
    };
    
    //布告板具体类:气象统计类
    class WeatherStatisticsDisplay:public Observer,public DisplayElement{
    private:
    	float avgTemperatuere,maxTemperature,minTemperature;
    	Subject *weatherData;
    
    public: 
    	WeatherStatisticsDisplay(Subject *weatherData){
    		this->weatherData = weatherData;
    		weatherData->registerObserver(this);
    	}
    
    	~WeatherStatisticsDisplay(){}
    
    	void update(float temp,float humi,float pressure){
    		if (1 == setConditionTimes)
    		{
    			this->avgTemperatuere = temp;
    			this->maxTemperature = temp;
    			this->minTemperature = temp;
    		}
    		else{
    			this->avgTemperatuere = (this->avgTemperatuere * (setConditionTimes - 1) + temp) / setConditionTimes;
    			this->maxTemperature = this->maxTemperature > temp ? this->maxTemperature : temp;
    			this->minTemperature = this->minTemperature < temp ? this->minTemperature : temp;
    		}
    
    		display();
    	}
    
    	void display(){
    		cout << "Avg/Max/Min: temperature  " << avgTemperatuere << "/" << 
    			maxTemperature << "/" <<minTemperature << endl;
    	}
    };
    
    int main()
    {
    	WeatherData *weatherData = new WeatherData();
    
    	CurrentConditionDisplay *currentConditionDisplay = new CurrentConditionDisplay(weatherData);
    	WeatherStatisticsDisplay *weatherStatisticsDisplay = new WeatherStatisticsDisplay(weatherData);
    	
    	weatherData->setMessurements(80,65,30.4f);
    	weatherData->setMessurements(82,70,29.2f);
    	weatherData->setMessurements(78,90,29.2f);
    
    	weatherData->removeObserver(weatherStatisticsDisplay);
    
    	cout << "after remove observer WeatherStatisticsDisplay:" << endl;
    	weatherData->setMessurements(80,65,30.4f);
    	weatherData->setMessurements(82,70,29.2f);
    	weatherData->setMessurements(78,90,29.2f);
    
    	return 0;
    }

    运行结果(VS2008 + Win7):


  • 相关阅读:
    vision transformer
    亮剑,gacutil.exe
    MOSS自定义登陆页面
    查看应用程序的进程ID
    基于AD的表单认证 Moss2010
    js 实现拖动
    获取指定数据库表, 以及指定表的数据信息
    二分法
    JZ053表示数值的字符串
    JZ054字符流中第一个不重复的字符
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3029444.html
Copyright © 2011-2022 走看看