观察者模式 :定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的多有依赖这都会收到通知并自动更新。
简单的说这种模式就像生活中的报社和订阅报纸的人一样 , 如果你订阅了报纸(登记为观察者),报社有有新报纸就会第一时间送给你。如果你退订(撤销观察者),报社将不在通知你
实现观察者模式的方法不止一种,但是以保管Subject和Observer接口的设计方式最常见。
下面以一个具体的气象站系统为例子 ,更好的说明这种模式如何使用
设计 1确定主题(会改变状态的) ,气象信息(weatherdata)比如 温度,湿度,压力,风速等等
2设计主题接口
public interface subject //主题接口 { public void registerObserver(observer o); //观察者作为变量,登记观察者 public void removeObserver(observer o); // 删除观察者 public void notifyObservers(observer o);//通知 观察者 ,当主题改变时这个方法会被调用通知观察者 }
3具体的主题类
public class weatherData:subject //具体的主题类实现主题接口 { private ArrayList observers;//添加一个arraylist来记录观察者,在构造函数中实例化 private float temprature;//添加主题对象需要更新数据的私有字段 private float humidity; private float pressure; public weatherData() { observers = new ArrayList(); } public void registerObserver(observer o) //注册观察者,只需要把它加入到arraylist中 { observers.Add(o); } public void removeObserver(observer o)// 观察者取消注册时,从list中删除 { // observers.Remove(o); int i = observers.IndexOf(o); if (i>=0) { observers.RemoveAt(i); } } public void notifyObservers()//通知每一个观察者,主题对象的状态改变,需要更新数据 { foreach (observer ob in observers) { ob.update(temprature, humidity, pressure); //观察者实现了update方法来更细数据 } } public void measurementsChanged() //当气象站得到更新的观察值,通知观察者 { notifyObservers(); } public void setMeasurements(float temprature, float humidity, float pressure)//模拟数据更新 { this.temprature = temprature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); //调用更新通知方法 } //其他方法 }
4 确定观察者(需要用到主题状态数据的对象)
5 观察者接口
public interface observer //观察者接口 { public void update(float temp,float humidity,float pressure); }
在添加一个接口用来显示 数据
public interface displayElement //这个接口只有一个方法用来更新显示数据的 { public void display(); }
6 具体观察者,只举一个例子
public class CurrentConditionsDisplay:observer,displayElement { private float humidity; //根据需要选择主题提供的数据 private float pressure; private subject weatherdata; // 主题对象 ,在构构造函数中实例化 public CurrentConditionsDisplay(subject weatherdata) { this.weatherdata = weatherdata;//保存 主题 对象的引用 ,使得观察者以后可以撤销注册 weatherdata.registerObserver(this);//登记自己 } public void update(float temp, float humidity, float pressure) { this.humidity = humidity; //把更新的数据保存到私有成员中 this.pressure = pressure; display(); // 然后调用display()
//通知时需要的其他方法
} public void display(){ } }
测试
protected void Page_Load(object sender, EventArgs e) { weatherData weatherData = new weatherData();//首先建立一个主题对象 CurrentConditionsDisplay ccd = new CurrentConditionsDisplay(weatherData);//建立一个观察者登记在主题对象上 weatherData.setMeasurements(11, 22, 33);//模拟主题对象数据更新 ,然后ccd对象会自动收到通知做出相应动作 }
要点 :1观察者模式定义对象之间1对多的关系
2主题用一个共同的接口更新观察者
3主题不需要知道观察者的内部细节,只需要知道他实现可观察者接口
遵循的设计原则: 1封装变化,将固定不变和变化分离,在观察者模式中,会改变的是主题的状态,以及观察者的数目和类型。用这个模式你可以改变依赖于主题状态的对象,
却不必改变主题。这就叫提前规划
2针对接口编程,而不是针对实现,主题与观察者都使用接口,观察者利用主题接口,向主题注册,而主题利用观察者接口通知观察者,这样可以让两者之间
运作正常,又具有松耦合的特点。
3多用组合,少用继承(观察者和主题如何搭配的),观察者模式利用“组合”将许多观察者组合进主题中,对象之间的这种关系不是通过继承产生的,而是在运
行时利用组合的方式产生的。