---------------------------------根据《Head First 设计模式》整理记忆----------------------------------
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新(是不是感觉很神奇!!!)。
小A公司接了一个为气象站开发Internet气象观测站的项目。项目开发要求:由气象站提供一个WeatherData对象负责追踪目前的天气状况(温度、湿度、气压),要求公司开发一个应用,有三种布告板,分别及时更新显示目前的状况,而且这个应用能够易于扩展开发新的布告板。
现在我们开始开发吧
1.第一次尝试
class WeatherData { public void measurementsChanged() { //取得最新测量值 float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); //更新到各个布告板 CurrentConditionsDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure); forecastDisplay.update(temp, humidity, pressure); } //这里是其他WeatherData方法 }
解析下:代码看上去蛮漂亮的,但是想想我们的设计原则
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2.针对接口编程,而不是针对实现编程。
2.第二次尝试:观察者模式
观察者模式主要的两个对象是主题和观察者,观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格观察者可能因此新值而更新。
接下来我们来看下观察者模式的一种常见做法:Subject和Observer接口的类设计的做法。
public interface Subject { void registerObserver(Observer o);//以观察者作为变量,该观察者是用来注册的。 void removeObserver(Observer o);//以观察者作为变量,该观察者是用来删除的。 void notifyObservers();//当主题状态改变时,这个方法会被调用,以通知所有观察者。 }
public interface Observer { void update(float temp,float humidity,float pressure);//所有观察者都必须实现updata()方法,以实现观察者接口。 }
class WeatherData:Subject { //ArrayList 来记录依赖主题的所有观察者 private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList(); } #region Subject 成员 //当注册观察者时,我们只要把它加到ArrayList的后面即可。 public void registerObserver(Observer o) { observers.Add(o ); } //当删除观察者时,我们只要把它从ArrayList中删除即可。 public void removeObserver(Observer o) { int i = observers.IndexOf(o ); if (i >=0) { observers.Remove(i ); } } //有趣的地方来了!在这里,我们把状态告诉每一个观察者。 //因为观察者都实现了update(),所以我们知道如何通知它们。 public void notifyObservers() { for (int i = observers.Count-1;i >=0 ;i -- ) { Observer observer = (Observer)observers[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(); } #endregion }
public interface DisplayElement
{
void display();//当布告板显示时,调用这个方法。
}
//这里我就写了一个布告板,有兴趣的朋友可以自己多写几个布告板,体验下观察者模式的精髓。 class CurrentConditionsDisplay:Observer ,DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData)//构造器需要 weatherData对象,作为注册之用。 { this.weatherData = weatherData; weatherData.registerObserver(this ); } #region DisplayElement 成员 //把最近的温度和湿度显示出来 public void display() { Console.WriteLine("Current conditions:"+temperature + "F degrees and"+humidity +"% humidity"); } #endregion #region Observer 成员 //当update()被调用时,我们把温度和湿度保存起来,然后调用display(). public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } #endregion }
测试下:
static void Main(string[] args) { WeatherData weatherData = new WeatherData();//首先,建立一个WeatherData对象 //建立一个布告板并把WeatherData对象传给它,currentDisplay 在构造器中往WeatherData对象注册自己 CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData ); //模拟新的气象测量 weatherData.setMeasurements(80,65,30.4f); weatherData.setMeasurements(82,70,29.2f); weatherData.setMeasurements(78,90,29.2f); Console.ReadKey(); }
调试显示:
----------------------------------分割线,又重新写了一下代码------------------------------------------------------------
首先三个接口:
1.subject接口,主题接口,当自己更新的时候用来发送给观察者资料,因此观察者需要过来注册一下,否则主题大人不知道,也可以不订这个主题,当变动的时候,需要通知他们,因此至少三个method。
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午7:44:23 类说明
*/
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
2.Obsever接口,当subject变化的时候,Observer需要提供update方法,使得主题可以给他们发送及时信息。
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午7:45:56 类说明
*/
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
3.DisplayElement接口,用来显示状态的接口
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午7:47:56 类说明
*/
public interface DisplayElement {
public void display();
}
4.WeatherData类,实现Subject接口
package ObserverPattern;
import java.util.ArrayList;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午7:48:41 类说明
*/
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {//注册到这个地方
observers = new ArrayList<Observer>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
5.测试类
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午8:04:51 类说明
*/
public class testMain {
public static void main(String[] args) {
WeatherData aTest = new WeatherData();
CuurentConditionDisplay aCuurent = new CuurentConditionDisplay(aTest);
StaticticsDisplay aStatictics = new StaticticsDisplay(aTest);
ForecastDisplay aForecast = new ForecastDisplay(aTest);
aTest.setMeasurements(11, 12, 13);
}
}
6.三个实现Oberserver,DisplayElement接口的显示状态的类:
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午8:08:13 类说明
*/
public class CuurentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject WeatherData;
public CuurentConditionDisplay(Subject WeatherData){
this.WeatherData = WeatherData;
WeatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
};
public void display() {
System.out.println("--------CuurentConditionDisplay-------------");
System.out.println("temperature:" + temperature + "humidity:"
+ humidity + "pressure:" + pressure);
};
}
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午8:08:13 类说明
*/
public class StaticticsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject WeatherData;
public StaticticsDisplay(Subject WeatherData){
this.WeatherData = WeatherData;
WeatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
};
public void display() {
System.out.println("--------StaticticsDisplay-------------");
System.out.println("temperature:" + temperature + "humidity:"
+ humidity + "pressure:" + pressure);
};
}
package ObserverPattern;
/**
* @author 作者 E-mail:
* @version 创建时间:2015年7月9日 下午8:08:13 类说明
*/
public class ForecastDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject WeatherData;
public ForecastDisplay(Subject WeatherData) {
this.WeatherData = WeatherData;
WeatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
};
public void display() {
System.out.println("--------ForecastDisplay-------------");
System.out.println("temperature:" + temperature + "humidity:"
+ humidity + "pressure:" + pressure);
};
}
观察者模式总结
优点
1. 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
缺点
1.依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
适用场景
1 当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
2 一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
---------附全部源代码:http://download.csdn.net/detail/bingbing8219/8885141------------------------------