设计模式之观察者模式
上一篇:设计模式之策略模式
故事要从气象站说起,气象站有个WeatherData对象,这个对象负责拿到所有的气象数据(温度、湿度、气压),而气象站同时也存在好几个公告板,负责显示气象数据,并且要实时更新。
很快我们便设计了下面的类图:
在WeatherData类中放一个公布板(CurrentDisplay)属性,在每次数据更新的时候调用notify方法来通知公布版更新实时数据。
很快的就可以将我们的气象站设计出来,在WeatherData类中放入一个CurrentDisplay对象,每次更新数据就通知我们的公告板对象进行更新数据。
package shejimoshi.Observer;
public class WeatherData {
private float teperature;
private float hunidity;
private float pressure;
private CurrentDisplay display;
WeatherData(CurrentDisplay display){
this.display = display;
}
public void notifyDisplay(){
this.display.update(teperature,hunidity,pressure);
}
public float getTeperature() {
return teperature;
}
public void setTeperature(float teperature) {
this.teperature = teperature;
}
public float getHunidity() {
return hunidity;
}
public void setHunidity(float hunidity) {
this.hunidity = hunidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package shejimoshi.Observer;
public class CurrentDisplay {
private float teperature;
private float hunidity;
private float pressure;
public void update(float teperature,float hunidity,float pressure){
this.teperature = teperature;
this.hunidity = hunidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("Current conditions:"+teperature+"and "+hunidity+"%humidity");
}
public static void main(String[] args) {
CurrentDisplay display = new CurrentDisplay();
WeatherData data = new WeatherData(display);
data.setTeperature(30);
data.setHunidity(1.1f);
data.setPressure(1.3f);
data.notifyDisplay();
}
}
系统如期上线,刚开始没问题,可是过了一段时间,气象站增加了一个公告板(SecondDisplay)用来显示一些基于观测值(温度,湿度,气压…)的一些计算结果。
这个时候我们就会在我们的WeatherData中在加一个属性SecondDisplay,然后来了几十个显示板,类爆炸了,WeatherData被我们修改的千疮百孔,如果有一天气象站突然不用某个显示板了,又要去修改,完了。
这就违反了设计模式的原则:开闭原则开闭原则:使软件中的对象对扩展开放,对修改关闭
观察者模式
既然需要很多个显示板,那么观察者模式就在WeatherData中存放一个容器用来存放显示板(Display),然后各个显示板就实现Display接口,再来一个显示板的时候就直接将显示板加入到WeatherData中的容器中,很快就能实现下面的代码:
package shejimoshi.Observer;
//显示板接口
public interface Display {
public void update(float teperature,float hunidity,float pressure);
}
package shejimoshi.Observer;
//主题接口也就是WeatherData实现的接口
public interface Subjcet {
public void registerDisplay(Display d);
public void removeDisplay(Display d);
public void notifyDisplay();
}
package shejimoshi.Observer;
public class CurrentDisplay implements Display{
private float teperature;
private float hunidity;
private float pressure;
public void update(float teperature,float hunidity,float pressure){
this.teperature = teperature;
this.hunidity = hunidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("Current conditions:"+teperature+"and "+hunidity+"%humidity");
}
}
package shejimoshi.Observer;
import java.util.ArrayList;
public class WeatherData implements Subjcet{
private float teperature;
private float hunidity;
private float pressure;
private ArrayList displays;
private CurrentDisplay display;
WeatherData(){
displays = new ArrayList();
}
@Override
public void registerDisplay(Display d) {
displays.add(d);
}
@Override
public void removeDisplay(Display d) {
int i = displays.indexOf(d);
if(i>=0){
displays.remove(i);
}
}
public void notifyDisplay(){
for (Object o:displays) {
Display display = (Display)o;
display.update(teperature,hunidity,pressure);
}
}
public float getTeperature() {
return teperature;
}
public void setTeperature(float teperature) {
this.teperature = teperature;
}
public float getHunidity() {
return hunidity;
}
public void setHunidity(float hunidity) {
this.hunidity = hunidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public static void main(String[] args) {
CurrentDisplay display = new CurrentDisplay();
WeatherData data = new WeatherData();
data.registerDisplay(display);
data.setTeperature(30);
data.setHunidity(1.1f);
data.setPressure(1.3f);
data.notifyDisplay();
}
}
这样每来一个显示板就可以将显示板注册到我们的主题(WeatherData)中去,再主题更新数据就会通知显示板来更新数据,如果不需要某个显示板就直接remove掉,很灵活。
推、拉
系统如期上线,顺利运行,突然有一天,一个显示板不高兴了:每次你主题更新的时候都要通知我(推
),我显得很被动,我需要是,我每次需要数据的时候我自己去拿数据,并且,我如果要不订阅你(remove掉),我需要通知你WeatherData,然后才能remove,我需要的是自己注册与否是我自己来决定,就像订报纸一样,我不订阅了我自己直接取消就好了:
package shejimoshi.Observer;
public class CurrentDisplay implements Display{
private float teperature;
private float hunidity;
private float pressure;
private Subjcet subjcet;
CurrentDisplay(Subjcet subjcet){
this.subjcet = subjcet;
}
public void regist(){
subjcet.registerDisplay(this);
}
public void remove(){
subjcet.removeDisplay(this);
}
public void getData(){
subjcet.notifyDisplay();
}
public void update(float teperature,float hunidity,float pressure){
this.teperature = teperature;
this.hunidity = hunidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("Current conditions:"+teperature+"and "+hunidity+"%humidity");
}
}
这样在Display中放入一个WeatherData之后,每个显示板自己就可以决定什么时候需要拿数据(拉
),以及自己决定自己是否订阅这个Subjcet。
最后
在jdk中的观察者模式是这样的:
Observer是个接口,Observable是个类,因为是类,所以不可以在继承Observable的时候再去继承别的类的特性,所以这就有很大的局限性,这边就不具体展开说了。
还没有评论,来说两句吧...