观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式中,subject是具有状态的对象,是真正拥有数据的对象。有许多观察者依赖着subject,依赖者subject的数据,一旦数据得到更新,subject便会通知各个观察者,并让观察者自动更新。这相当于数据只由subject控制,所有观察者并不知道具体数据,它们只是等待subject的通知,只要得到subject的通知,它们就会自动更新。其中这里涉及对象的处理,所以会使用到对象集合;另外,如果有观察者需要依赖subject,则需要进行注册,即加入subject通知的对象集合中,反之则删除。观察者模式中,其实对象之间依赖程度降到了很低,因为观察者之间没有联系,而且subject也不与观察者直接联系,subject不清楚具体有哪些观察者对象,而是通过集合的遍历告诉各个对象数据发生了改变。也就是说对象之间的依赖变成了subject对象与观察者列表之间的依赖,这样的话降低了耦合度,对象之间无须清楚对方的具体情况。
1.适用性和优缺点
c.观察者模式符合“开闭原则”的要求。
3.缺点:
a.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
b.如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进 行循环调用,可能导致系统崩溃。
c.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
2.示例讲解
package subject;
import observer.Observer;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
subject只需要三个函数即可,一个是供观察者注册的,一个是供观察者注销的,还有一个便是subject数据发生变化时,通知观察者的。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!
从本文例子可以知道观察者模式一对多的依赖特点,其次就是subject具有广播的作用,一旦数据发生改变,便会遍历对象列表,通知各个观察者进行数据更新。而对于观察者来说,只需要进行初始化时需要注册进对象列表,以便称为subject通知的对象。