观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式中,subject是具有状态的对象,是真正拥有数据的对象。有许多观察者依赖着subject,依赖者subject的数据,一旦数据得到更新,subject便会通知各个观察者,并让观察者自动更新。这相当于数据只由subject控制,所有观察者并不知道具体数据,它们只是等待subject的通知,只要得到subject的通知,它们就会自动更新。其中这里涉及对象的处理,所以会使用到对象集合;另外,如果有观察者需要依赖subject,则需要进行注册,即加入subject通知的对象集合中,反之则删除。观察者模式中,其实对象之间依赖程度降到了很低,因为观察者之间没有联系,而且subject也不与观察者直接联系,subject不清楚具体有哪些观察者对象,而是通过集合的遍历告诉各个对象数据发生了改变。也就是说对象之间的依赖变成了subject对象与观察者列表之间的依赖,这样的话降低了耦合度,对象之间无须清楚对方的具体情况。
1.适用性和优缺点
1.适用性:
a.当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
b.当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
c.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。
2.优点:
a.
当两个对象之间送耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间送耦合。主题所知道只是一个具体的观察者列表,每一个具体观察者都符合一个抽象观察者的接口。主题并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
b.观察者模式支持“广播通信”。主题会向所有的观察者发出通知。
c.观察者模式符合“开闭原则”的要求。
3.缺点:
a.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
b.如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进 行循环调用,可能导致系统崩溃。
c.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
2.示例讲解
本文举一个天气预报的例子,假设天气数据是subject,那么各种天气预报的显示都是观察者,一旦天气数据发生改变,那么它们显示都会发生改变。
首先先定义subject接口:
package subject;
import observer.Observer;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
subject只需要三个函数即可,一个是供观察者注册的,一个是供观察者注销的,还有一个便是subject数据发生变化时,通知观察者的。
接下来是观察者类Observer接口:
package observer;
public interface Observer {
public void update(float temperature, float humidity, float pressure);
}
observer接口只有一个方法,因为观察者类被通知数据发生改变之后,只需要进行更新就行。但是,天气预报需要呈现出来,这里再提供一个显示天气数据的观察者类,以下是DisplayElement接口:
package observer;
public interface DisplayElement {
public void display();
}
接下来是subject的具体类,具体类中除了实现subject接口的三个方法之外,还有一个设置参数的方法是便于新数据的获取;有一个调用通知观察者的方法是便于外部调用从而将通知观察者的方法进行封装:
package subject;
import java.util.*;
import observer.Observer;
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData()
{
observers=new ArrayList();//初始化对象列表,用于遍历以通知观察者。
}
//注册观察者
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
observers.add(o);
}
//注销观察者
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
int i=observers.indexOf(o);
if(i>=0)
{
observers.remove(i);
}
}
//通知观察者。
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i=0;i<observers.size();i++)
{
Observer observer=(Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
//当从气象站得到更新值的时候,通知观察者。
public void measurementsChanged()
{
notifyObservers();
}
//获取新数据,通知相应函数以便通知观察者
public void setMeasurements(float temperature,float humidity,float pressure)
{
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
接下来是三个观察者类,这里的观察者类实现Observer和DisplayElement接口:
package observer;
import subject.Subject;
public class CurrentConditionsDisplay implements DisplayElement, Observer {
private float temperature;
private float humidity;
private Subject weatherData;
//构造器需要weatherData对象作为注册用。
public CurrentConditionsDisplay(Subject weatherData)
{
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
//当updata()被调用时,将更新的温度和湿度保存,并调用display()
public void update(float temperature, float humidity, float pressure) {
// TODO Auto-generated method stub
this.temperature=temperature;
this.humidity=humidity;
display();
}
@Override
//显示最新的温度和湿度
public void display() {
// TODO Auto-generated method stub
System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
}
}
package observer;
import subject.Subject;
public class ForecastDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
//传入subject对象用于观察者对象进行注册。
public ForecastDisplay(Subject weatherData)
{
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
// TODO Auto-generated method stub
if (temperature>=80 && humidity<70)
{
System.out.println("Forecast: Improving weather on the way!");
}
else if(temperature<=90&&temperature>=80 && humidity>=70)
{
System.out.println("Forecast: Watch out for cooler,rainy weather!");
}
else
{
System.out.println("Forecast: More of the same!");
}
}
@Override
public void update(float temperature, float humidity, float pressure) {
// TODO Auto-generated method stub
this.temperature=temperature;
this.humidity=humidity;
display();
}
}
package observer;
import subject.Subject;
import java.util.*;
public class StatisticsDisplay implements Observer, DisplayElement {
private float temperature;
private Subject weatherData;
private ArrayList temp;
public StatisticsDisplay(Subject weatherData)
{
this.weatherData=weatherData;
temp=new ArrayList();
weatherData.registerObserver(this);
}
@Override
public void display() {
// TODO Auto-generated method stub
float max_temp=(float)temp.get(0);
float min_temp=(float)temp.get(0);
float sum_temp=0.0f;
for(int i=0;i<temp.size();i++)
{
float elements=(float)temp.get(i);
sum_temp+=elements;
if(elements>max_temp)
{
max_temp=elements;
}
else if(elements<min_temp)
{
min_temp=elements;
}
}
//求平均气温
double avg_temp=sum_temp/((float)temp.size());
System.out.println("Avg/Max/Min temperature = "+avg_temp+"/"+max_temp+"/"+min_temp);
}
@Override
public void update(float temperature, float humidity, float pressure) {
// TODO Auto-generated method stub
this.temperature=temperature;
temp.add(this.temperature);
display();
}
}
最后是客户端代码,客户端主要是为了让观察者注册进subject的对象列表,然后对subject进行更新数据,subject类会负责通知各个观察者对象,观察者对象会作出更新,并呈现出结果。
package observer;
import subject.WeatherData;
public class WeatherStation {
public static void main(String[] args) {
// TODO Auto-generated method stub
//观察者对象进行注册
WeatherData weatherData=new WeatherData();
CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay=new ForecastDisplay(weatherData);
//数据更新,subject获取新数据,接着便会通知观察者,然后观察者进行更新数据并呈现结果。
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82,70,29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
运行程序,得到如下结果:
Current conditions:80.0F degrees and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast: Improving weather on the way!
Current conditions:82.0F degrees and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast: Watch out for cooler,rainy weather!
Current conditions:78.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast: More of the same!
由上面WeatherStation类可以看出,前面声明WeatherDate对象,然后声明观察者对象,并将WeatherDate对象传入,就是为了在观察者对象初始化时进行注册,注册进subject类的观察者对象列表中。然后调用weatherDate对象的setMeasurements()函数,则是更新WeatherDate类的数据,观察WeatherDate类:更新数据之后,WeatherDate类的setMeasurements()函数会调用measurementsChanged()方法,然后measurementsChanged()方法中会调用通知各观察者更新的方法。完成了subject类获取数据-->通知观察者-->观察者数据更新-->观察者呈现结果的整个流程。
从本文例子可以知道观察者模式一对多的依赖特点,其次就是subject具有广播的作用,一旦数据发生改变,便会遍历对象列表,通知各个观察者进行数据更新。而对于观察者来说,只需要进行初始化时需要注册进对象列表,以便称为subject通知的对象。
从本文例子可以知道观察者模式一对多的依赖特点,其次就是subject具有广播的作用,一旦数据发生改变,便会遍历对象列表,通知各个观察者进行数据更新。而对于观察者来说,只需要进行初始化时需要注册进对象列表,以便称为subject通知的对象。