首先来看看最简单的观察者模式,使用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{
public void update(Observable o, Object arg)
{
//arg为接收到的消息,收到消息直接print
System.out.println("A received:" + arg);
}
}
/**
* 观察者B
*/
class ObserverB implements Observer{
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
2B received: hello
A received: hello
上面的代码很简单,Subject
类实现了Observable
接口,表明了它是一个主题(被观察者)。然后,它本身持有了两个观察者的引用 ObserverA
和 ObserverB
。当Subject
类的状态改变时,观察者将会调用update()
方法来进行相应的操作。
注意,Subject
类在通知观察者的时候,只知道这些是观察者(实现了 Observer
)接口,但是并不知道是哪个观察者。同时,它也不知道观察者的具体实现,这样就实现了主题和观察者之间的松耦合。下面详细介绍观察者模式的使用场景以及实现。
观察者模式
观察者模式的设计意图
定义对象之间的一对多依赖关系,这样当一个对象改变状态时,所有的依赖项都会自动得到通知和更新。
使用场景
在以下情况,可以考虑使用观察者模式:
- 当一个问题的抽象后有两个主要操作A和B,一个依赖于另一个。将这些操作封装在单独的对象中,可以可以独立地进行更改和重用。
- 当对一个对象的状态改变时,需要更改其他对象,但是不知道需要更改哪些对象。
- 希望实现两个对象之间的松耦合。
在上面的描述中,最典型的使用场景就是一个对象的改变,需要将其他对象也进行相应的操作(主题通知观察者)。
具体实例
天气 Weather
的改变将会导致其他物种的改变,半兽人 Orcs
和霍比特人Hobbits
对于天气的改变将会做出不同的反应。在这个实例中,天气就是一个主题,也就是被观察者;半兽人和霍比特人就是观察者,他们会随着天气的变化来相应的变化。
类之间的关系图如下图所示:WeatherObserver
接口定义了一个update()
方法。两个观察者Hobbits
和Orcs
实现了该接口。主题类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);
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);
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;
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
25public 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
1217: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.