设计模式-观察者模式

观察者模式

主题对象管理某些数据,观察者对象注册主题,当主题内的数据改变时,就会通知观察者对象,以便观察者对象在主题对象获取新数据时能够第一时间收到更新数据,注意的是观察者对象一定要注册对应的主题对象,如果不想继续获得数据了,则必须要取消注册,那么就不再监听主题对象的数据变化了

观察者模式定义了对象之间的一对多依赖,这 样一来,当一个对象改变状态时,它的所有依赖者都 会收到通知并自动更新

Demo

目的:WeatherData对象数据变化,会通知注册该对象的模版,然后对应的模版显示变化的数据

步骤

1.定义一个主题接口,其中包含注册、移除、通知观察者,所有的主题都需要实现这个接口

1
2
3
4
5
6
7
8
public interface Subject {
//注册
void RegisterObject(Observer o);
//移除
void RemoveObject(Observer o);
//通知
void notifyObject();
}

2.主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 RegisterObject(Observer o) {
observers.add(o);
}
//将观察者移除链表
@Override
public void RemoveObject(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}

public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}

@Override
public void notifyObject() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temperature, humidity, pressure);
}
}

//一旦气象测量更新,此方法会被调用
void measurementsChanged() {
notifyObject();
}
}

3.定义一个观察者接口,所有观察者都必须实现这个类,实现update方法

1
2
3
4
public interface Observer {
//更新数据
void update(float temp, float humidity, float pressure);
}

4.定义一个显示的接口,所有观察者都必须实现这个类

1
2
3
public interface DisplayElement {
void display();
}

5.编写观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//显示当前观测值
public class CurrentConditionsDisplay implements Observer, DisplayElement {

private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;

//作为注册之用
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.RegisterObject(this);
}

@Override
public void display() {
System.out.println("温度" + temperature + "湿度" + humidity + "气压" + pressure);
}

@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
}

6.编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
--------------------------------------------------------------------结果
温度80.0湿度65.0气压30.4
温度82.0湿度70.0气压29.2
温度78.0湿度90.0气压29.2


//删除该观察者
weatherData.RemoveObject(currentConditionsDisplay);
--------------------------------------------------------------------结果
无显示

7.java也有内置的观察者接口(java.util包内包含最基本的Observer接口与Observable类)

Observer接口(观察者实现它)有一个void update(Observable o, Object arg);方法

Observable类(主题继承它)的方法,如下图

主题推送通知

其中setChanged()方法,标记状态已经改变的事实,如果调用notifyObservers()之前没有先调用setChanged(),观察者就“不会”被通知

然后调用两种notifyObservers()方法中的一个: notifyObservers()notifyObservers(Object arg)

观察者接收通知

void update(Observable o, Object arg);

  • Observable o主题本身当作第一个变量, 好让观察者知道是哪个主 题通知它的
  • Object arg这正是传入notifyObservers()的数据对象。 如果没有说明则为空

推和拉模式的区别与使用

  • 推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

  • 推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。所以推荐使用拉模型

赏个🍗吧
0%