观察者模式

首先来看看最简单的观察者模式,使用java.util包实现的观察者模式。使用另个类,一个是java.util.Observer接口以及java.util.Observable来实现。(除了直接使用java提供的现成的观察者类以外,我们也可以自己实现观察者模式)直接上代码:

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
/**
* 观察者A
*/
class ObserverA implements Observer{
@Override
public void update(Observable o, Object arg)
{
//arg为接收到的消息,收到消息直接print
System.out.println("A received:" + arg);
}
}
/**
* 观察者B
*/
class ObserverB implements Observer{
@Override
public void update(Observable o, Object arg)
{
System.out.println("B received:" + arg);
}
}
/**
* 主题类,也就是被观察的类
* 直接继承自Observable接口
*/
public class Subject extends Observable
{

{
//添加观察者
addObserver(new ObserverA());
addObserver(new ObserverB());
//设置状态已经改变
setChanged();
}
public static void main(String[] args)
{
Subject subject = new Subject();
subject.notifyObservers("hello");
}
}

运行结果:

1
2
B received: hello
A received: hello

上面的代码很简单,Subject类实现了Observable接口,表明了它是一个主题(被观察者)。然后,它本身持有了两个观察者的引用 ObserverAObserverB 。当Subject 类的状态改变时,观察者将会调用update()方法来进行相应的操作。
注意,Subject 类在通知观察者的时候,只知道这些是观察者(实现了 Observer )接口,但是并不知道是哪个观察者。同时,它也不知道观察者的具体实现,这样就实现了主题和观察者之间的松耦合。下面详细介绍观察者模式的使用场景以及实现。

观察者模式

观察者模式的设计意图

定义对象之间的一对多依赖关系,这样当一个对象改变状态时,所有的依赖项都会自动得到通知和更新。

使用场景

在以下情况,可以考虑使用观察者模式:

  • 当一个问题的抽象后有两个主要操作A和B,一个依赖于另一个。将这些操作封装在单独的对象中,可以可以独立地进行更改和重用。
  • 当对一个对象的状态改变时,需要更改其他对象,但是不知道需要更改哪些对象。
  • 希望实现两个对象之间的松耦合。

在上面的描述中,最典型的使用场景就是一个对象的改变,需要将其他对象也进行相应的操作(主题通知观察者)。

具体实例

天气 Weather的改变将会导致其他物种的改变,半兽人 Orcs和霍比特人Hobbits对于天气的改变将会做出不同的反应。在这个实例中,天气就是一个主题,也就是被观察者;半兽人和霍比特人就是观察者,他们会随着天气的变化来相应的变化。
类之间的关系图如下图所示:
观察者之间的类图
WeatherObserver接口定义了一个update()方法。两个观察者HobbitsOrcs实现了该接口。主题类Weather中有增加、删除、通知观察者的方法,当timePasses方法执行时,将会通知已经注册的观察者;WeatherType是一个天气的枚举类。具体实现代码如下:

1
2
3
4
5
6
7
8
9
10
/**
*
* 观察者接口
*
*/
public interface WeatherObserver {

void update(WeatherType currentWeather);

}

两个观察者Hobbits和Orcs。

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
47
48
49
50
51
52
53
54
55
56
57
58
/**
*
* Hobbits
*
*/
public class Hobbits implements WeatherObserver {

private static final Logger LOGGER = LoggerFactory.getLogger(Hobbits.class);

@Override
public void update(WeatherType currentWeather) {
switch (currentWeather) {
case COLD:
LOGGER.info("The hobbits are shivering in the cold weather.");
break;
case RAINY:
LOGGER.info("The hobbits look for cover from the rain.");
break;
case SUNNY:
LOGGER.info("The happy hobbits bade in the warm sun.");
break;
case WINDY:
LOGGER.info("The hobbits hold their hats tightly in the windy weather.");
break;
default:
break;
}
}
}
/**
*
* Orcs
*
*/
public class Orcs implements WeatherObserver {

private static final Logger LOGGER = LoggerFactory.getLogger(Orcs.class);

@Override
public void update(WeatherType currentWeather) {
switch (currentWeather) {
case COLD:
LOGGER.info("The orcs are freezing cold.");
break;
case RAINY:
LOGGER.info("The orcs are dripping wet.");
break;
case SUNNY:
LOGGER.info("The sun hurts the orcs' eyes.");
break;
case WINDY:
LOGGER.info("The orc smell almost vanishes in the wind.");
break;
default:
break;
}
}
}

下面是主题类Weather的定义:

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
47
48
49
50
51
52
53
54
55
56
/**
*
* 天气的枚举
*
*/
public enum WeatherType {

SUNNY, RAINY, WINDY, COLD;

@Override
public String toString() {
return this.name().toLowerCase();
}
}
/**
*
* Weather can be observed by implementing {@link WeatherObserver} interface and registering as
* listener.
*
*/
public class Weather {

private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class);

private WeatherType currentWeather;
private List<WeatherObserver> observers;

public Weather() {
observers = new ArrayList<>();
currentWeather = WeatherType.SUNNY;
}

public void addObserver(WeatherObserver obs) {
observers.add(obs);
}

public void removeObserver(WeatherObserver obs) {
observers.remove(obs);
}

/**
* Makes time pass for weather
*/
public void timePasses() {
WeatherType[] enumValues = WeatherType.values();
currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length];
LOGGER.info("The weather changed to {}.", currentWeather);
notifyObservers();
}

private void notifyObservers() {
for (WeatherObserver obs : observers) {
obs.update(currentWeather);
}
}
}

测试类的代码:

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
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {

//1. 定义了一个主题
Weather weather = new Weather();
//2. 添加两个观察者
weather.addObserver(new Orcs());
weather.addObserver(new Hobbits());

//3. 当主题改变时看观察者的变化
weather.timePasses();
weather.timePasses();
weather.timePasses();
weather.timePasses();

}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
17:27:44.181 [main] INFO com.iluwatar.observer.Weather - The weather changed to rainy.
17:27:44.185 [main] INFO com.iluwatar.observer.Orcs - The orcs are dripping wet.
17:27:44.186 [main] INFO com.iluwatar.observer.Hobbits - The hobbits look for cover from the rain.
17:27:44.186 [main] INFO com.iluwatar.observer.Weather - The weather changed to windy.
17:27:44.186 [main] INFO com.iluwatar.observer.Orcs - The orc smell almost vanishes in the wind.
17:27:44.186 [main] INFO com.iluwatar.observer.Hobbits - The hobbits hold their hats tightly in the windy weather.
17:27:44.186 [main] INFO com.iluwatar.observer.Weather - The weather changed to cold.
17:27:44.186 [main] INFO com.iluwatar.observer.Orcs - The orcs are freezing cold.
17:27:44.186 [main] INFO com.iluwatar.observer.Hobbits - The hobbits are shivering in the cold weather.
17:27:44.186 [main] INFO com.iluwatar.observer.Weather - The weather changed to sunny.
17:27:44.186 [main] INFO com.iluwatar.observer.Orcs - The sun hurts the orcs' eyes.
17:27:44.186 [main] INFO com.iluwatar.observer.Hobbits - The happy hobbits bade in the warm sun.