文章目录
一、观察者模式简介
Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。
Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
二、观察者模式的结构
三、观察者模式角色与职责
- Subject(被观察者): 被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
- ConcreteSubject : 被观察者的具体实现。包含一些基本的属性状态及其他操作。
- Observer(观察者):接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
- ConcreteObserver :观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
四、观察者模式的具体实现
Internet气象站项目:
- 提供温度、气压和湿度的接口
- 测量数据更新时需时时通知给第三方
- 需要设计开放型API,便于其他第三方公司也能接入气象站获取数据
1、不使用观察者模式
方案设计:
Current当前温度信息类 | Weather天气信息类 |
---|---|
update() | getTem() |
display() | getHum() |
- | getPre() |
- | dataChange() |
在Weather类中,当发生数据变化,调用dataChange()方法时,同时通知Current类中的update()方法,并调用display()方法打印出天气。
Weather类代码示例:
// An highlighted block
package design.sob.gys.nosob;
public class Weather {
private float tem;
private float hum;
private float pre;
private Current current;
public Weather(Current current) {
this.current=current;
}
public float getTem() {
return tem;
}
public void setTem(float tem) {
this.tem = tem;
dataChange();
}
public float getHum() {
return hum;
}
public void setHum(float hum) {
this.hum = hum;
dataChange();
}
public float getPre() {
return pre;
}
public void setPre(float pre) {
this.pre = pre;
dataChange();
}
public void setAll(float tem,float hum,float pre)
{
this.tem=tem;
this.hum=hum;
this.pre=pre;
dataChange();
}
public void dataChange()
{
current.update(getTem(),getHum(),getPre());
}
}
我们在每次天气信息有变化时都通知Current类,并主动推送天气信息。接下来是定义Currnet类:
// An highlighted block
package design.sob.gys.nosob;
public class Current {
private float tem;
private float hum;
private float pre;
public void update(float tem,float hum,float pre) {
this.tem=tem;
this.hum=hum;
this.pre=pre;
display();
}
public void display() {
System.out.println("今日温度"+tem);
System.out.println("今日适度"+hum);
System.out.println("今日气压"+pre);
}
}
Current类中只有两个方法,接受更新,并且打印信息。
在这个代码中,若是我们想要增加天气预报类,那么需要在定义Weather类时,需要增加构造方法的参数,当显示天气的类较多时,代码需要大量修改,无法在运行时动态添加,代码的可用性差。
接下来实现观察者模式。
2、使用观察者模式
对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化。
方案设计:
接口设计
Subject | Observer |
---|---|
registerOb()登记 | update()更新 |
removeOb()移除 | - |
notifaOb()通知 | - |
Subject 接口:
// An highlighted block
package design.sob.gys.subject;
import design.sob.gys.observer.Observer;
public interface Subject {
void registerOb(Observer o);
void removeOb(Observer o);
void notifyOb();
}
Observer接口:
// An highlighted block
package design.sob.gys.observer;
public interface Observer {
void update(float tem, float hum, float pre);
}
实现类设计
Weather implement Subject | Current implement Observer | Forecast implement Observer |
---|---|---|
registerOb() | update() | update |
removeOb() | display() | display() |
notifyOb() | - | - |
getTem() | - | - |
getHum() | - | - |
getPre() | - | - |
Weather类实现Subject接口:
// An highlighted block
package design.sob.gys.subject;
import java.util.LinkedList;
import design.sob.gys.observer.Observer;
public class Weather implements Subject{
private float tem;
private float hum;
private float pre;
private LinkedList<Observer> observers;//使用LinkedList存储注册用户
public Weather() {
super();
observers=new LinkedList<Observer>();
}
@Override
public void registerOb(Observer o) {
// TODO Auto-generated method stub
observers.add(o);
}
@Override
public void removeOb(Observer o) {
// TODO Auto-generated method stub
observers.remove(o);
}
@Override
public void notifyOb() {
// TODO Auto-generated method stub
for(Observer o :observers)
o.update(getTem(),getHum(),getPre());
}
public float getTem() {
return tem;
}
public void setTem(float tem) {
this.tem = tem;
notifyOb();
}
public float getHum() {
return hum;
}
public void setHum(float hum) {
this.hum = hum;
notifyOb();
}
public float getPre() {
return pre;
}
public void setPre(float pre) {
this.pre = pre;
notifyOb();
}
public void setAll(float tem,float hum,float pre) {
this.tem=tem;
this.hum=hum;
this.pre=pre;
notifyOb();
}
}
Current类实现Observer接口:
// An highlighted block
package design.sob.gys.observer;
public class Current implements Observer{
private float tem;
private float hum;
private float pre;
@Override
public void update(float tem, float hum, float pre) {
this.tem=tem;
this.hum=hum;
this.pre=pre;
display();
}
public void display() {
System.out.println("今日温度"+tem);
System.out.println("今日湿度"+hum);
System.out.println("今日气压"+pre);
}
}
Forecast类实现Observer接口:
// An highlighted block
package design.sob.gys.observer;
public class Forecast implements Observer{
private float tem;
private float hum;
private float pre;
@Override
public void update(float tem, float hum, float pre) {
this.tem=tem;
this.hum=hum;
this.pre=pre;
display();
}
public void display() {
System.out.println("明日温度"+(tem+1.5));
System.out.println("明日湿度"+(hum+12));
System.out.println("明日气压"+(pre-5));
}
}
测试程序:通过创建两个观察者current和forecast,并注册到weather对象中,每次天气更新,weather对象将发送信息到观察者,并显示。
// An highlighted block
package design.sob.gys.subject;
import design.sob.gys.observer.Current;
import design.sob.gys.observer.Forecast;
public class Testsob {
public static void main(String[] args) {
Current current =new Current();
Weather weather =new Weather();
weather.registerOb(current);
weather.setAll(38, 54, 112);
Forecast forecast=new Forecast();
weather.registerOb(forecast);
System.out.println("--------");
weather.setAll(25, 67, 115);
System.out.println("--------");
weather.setHum(78);
weather.removeOb(current);
System.out.println("--------");
weather.notifyOb();
}
}
以下是测试输出:
// An highlighted block
今日温度38.0
今日湿度54.0
今日气压112.0
--------
今日温度25.0
今日湿度67.0
今日气压115.0
明日温度26.5
明日湿度79.0
明日气压110.0
--------
今日温度25.0
今日湿度78.0
今日气压115.0
明日温度26.5
明日湿度90.0
明日气压110.0
--------
明日温度26.5
明日湿度90.0
明日气压110.0
可以看到,观察者模式可以灵活的接入其他对象,保证Weather实现不用每次更新。
五、Java内置观察者模式
内置观察者模式有两种方案:
- 消息通知,并推送消息
- 消息通知,观察者拉去消息
1、类 Observable
public class Observableextends Object此类表示模型视图范例中的 observable 对象,或者说“数据”。可将其子类化,表示应用程序想要观察的对象。
一个 observable 对象可以有一个或多个观察者。观察者可以是实现了 Observer 接口的任意对象。一个 observable 实例改变后,调用 Observable 的 notifyObservers 方法的应用程序会通过调用观察者的 update 方法来通知观察者该实例发生了改变。
未指定发送通知的顺序。Observable 类中所提供的默认实现将按照其注册的重要性顺序来通知 Observers,但是子类可能改变此顺序,从而使用非固定顺序在单独的线程上发送通知,或者也可能保证其子类遵从其所选择的顺序。
注意,此通知机制与线程无关,并且与 Object 类的wait 和 notify 机制完全独立。 新创建一个 observable 对象时,其观察者集是空的。当且仅当 equals 方法为两个观察者返回 true 时,才认为它们是相同的。
类方法
返回值 | 方法名 | 方法说明 |
---|---|---|
void | addObserver(Observer o) | 如果观察者与集合中已有的观察者不同,则向对象的观察者集中添加此观察者。 |
protected | void clearChanged() | 指示对象不再改变,或者它已对其所有的观察者通知了最近的改变,所以 hasChanged 方法将返回 false。 |
int | countObservers() | 返回 Observable 对象的观察者数目。 |
void | deleteObserver(Observer o) | 从对象的观察者集合中删除某个观察者。 |
void | deleteObservers() | 清除观察者列表,使此对象不再有任何观察者。 |
boolean | hasChanged() | 测试对象是否改变。 |
void | notifyObservers() | 如果 hasChanged 方法指示对象已改变,则通知其所有观察者,并调用 clearChanged 方法来指示此对象不再改变。 |
void | notifyObservers(Object arg) | 如果 hasChanged 方法指示对象已改变,则通知其所有观察者,并调用 clearChanged 方法来指示此对象不再改变。 |
protected | void setChanged() | 标记此 Observable 对象为已改变的对象;现在hasChanged 方法将返回 true。 |
2、接口 Observer
public interface Observer一个可在观察者要得到 observable 对象更改通知时可实现 Observer 接口的类。
接口方法
返回值 | 方法名 | 方法说明 |
---|---|---|
void | update(Observable o, Object arg) | 只要改变了 observable 对象就调用此方法。 |
3、内置观察者实现
仍然实现我们之前的天气预报例子。
我们首先定义一个用来保存天气信息的Data类:
// An highlighted block
package design.sob.gys.innernob;
public class Data {
private float tem;
private float hum;
private float pre;
public float getTem() {
return tem;
}
public void setTem(float tem) {
this.tem = tem;
}
public float getHum() {
return hum;
}
public void setHum(float hum) {
this.hum = hum;
}
public float getPre() {
return pre;
}
public void setPre(float pre) {
this.pre = pre;
}
}
注意,Observable有一个setChanged()方法,该方法返回一个boolean值,用来检测是否发生变化,在改变信息时,需要设置改boolean值并在传递消息结束后清除clearChanged()。
继承Observable类的Weather类:
// An highlighted block
package design.sob.gys.innernob;
import java.util.Observable;
public class Weather extends Observable{
private Data data;
public Weather() {
super();
data = new Data();
}
public float getTem() {
return data.getTem();
}
public void setTem(float tem) {
data.setTem(tem);
this.setChanged();// 设置Boolean值
this.notifyObservers(data);
this.clearChanged();//清除Boolean值
}
public float getHum() {
return data.getHum();
}
public void setHum(float hum) {
data.setHum(hum);
this.setChanged();
this.notifyObservers(data);
this.clearChanged();
}
public float getPre() {
return data.getPre();
}
public void setPre(float pre) {
data.setPre(pre);
this.setChanged();
this.notifyObservers(data);
this.clearChanged();
}
public void setAll(float tem,float hum,float pre) {
data.setTem(tem);
data.setHum(hum);
data.setPre(pre);
this.setChanged();
this.notifyObservers(data);
this.clearChanged();
}
}
实现Observer接口的Current类:
// An highlighted block
package design.sob.gys.innernob;
import java.util.Observable;
import java.util.Observer;
public class Current implements Observer{
private float tem;
private float hum;
private float pre;
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
this.tem=((Data)(arg)).getHum();
this.hum=((Data)(arg)).getTem();
this.pre=((Data)(arg)).getPre();
display();
}
public void display() {
System.out.println("今日温度"+tem);
System.out.println("今日湿度"+hum);
System.out.println("今日气压"+pre);
}
}
实现Observer接口的Forecast类:
// An highlighted block
package design.sob.gys.innernob;
import java.util.Observable;
import java.util.Observer;
public class Forecast implements Observer{
private float tem;
private float hum;
private float pre;
@Override
public void update(Observable o, Object arg) {
this.tem=((Data)(arg)).getHum();
this.hum=((Data)(arg)).getTem();
this.pre=((Data)(arg)).getPre();
display();
}
public void display() {
System.out.println("明日温度"+(tem+2.4));
System.out.println("明日湿度"+(hum-12));
System.out.println("明日气压"+(pre+10));
}
}
测试内置观察者模式:
// An highlighted block
package design.sob.gys.innernob;
public class Testinnersob {
public static void main(String[] args) {
// TODO Auto-generated method stub
Current current =new Current();
Forecast forecast = new Forecast();
Weather weather=new Weather();
weather.addObserver(current);
weather.addObserver(forecast);
weather.setAll(24, 45, 112);
System.out.println("---------");
weather.setPre(100);
System.out.println("---------");
weather.deleteObserver(current);
weather.setTem(-10);
}
}
测试结果输出如下:
// An highlighted block
明日温度47.4
明日湿度12.0
明日气压122.0
今日温度45.0
今日湿度24.0
今日气压112.0
---------
明日温度47.4
明日湿度12.0
明日气压110.0
今日温度45.0
今日湿度24.0
今日气压100.0
---------
明日温度47.4
明日湿度-22.0
明日气压110.0
可以看到,内置观察者的输出顺序与原来的不一致,原来使用LinkedList,先进先出。
六、Observer模式的典型应用
- 侦听事件驱动程序设计中的外部事件
- 侦听/监视某个对象的状态变化
- 发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者