Fork me on GitHub

设计模式之观察者模式

设计模式之观察者模式

1. 什么是观察者模式

Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。这样的话就不会错过该对象感兴趣的事情。对象甚至可以在运行时决定是否需要继续被通知。

Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。

1

  • Subject(被观察者)
    被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。 
    
  • ConcreteSubject
    被观察者的具体实现。包含一些基本的属性状态及其他操作。
    
  • Observer(观察者)
    接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
    
  • ConcreteObserver
    观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
    

2. 具体实例

首先我有一个Internet的气象站项目:

提供温度、气压和湿度的接口
测量数据更新时需时时通知给第三方
需要设计开放型API,便于其他第三方公司也能接入气象站获取数据

我首先按照面向对象的设计过程,我首先想到的是设计两个类,一个是天气的数据,一个就是公告板也就是对天气的数据进行操作的类

2

具体实现如下:

CurrentConditions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CurrentConditions {

private float mTemperature;
private float mPressure;
private float mHumidity;

public void update(float mTemperature,float mPressure,float mHumidity)
{
this.mTemperature=mTemperature;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
display();
}

public void display()
{
System.out.println("***Today mTemperature: "+mTemperature+"***");
System.out.println("***Today mPressure: "+mPressure+"***");
System.out.println("***Today mHumidity: "+mHumidity+"***");
}
}

WeatherData:

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
public class WeatherData {
private float mTemperature;
private float mPressure;
private float mHumidity;
private CurrentConditions currentConditions;

public WeatherData( CurrentConditions currentConditions){
this.currentConditions = currentConditions;
}
public float getmTemperature() {
return mTemperature;
}
public float getmPressure() {
return mPressure;
}
public float getmHumidity() {
return mHumidity;
}

public void dataChange(){
currentConditions.update( getmTemperature(),getmPressure(),getmHumidity());
}

public void setData(float mTemperature,float mPressure,float mHumidity)
{
this.mTemperature=mTemperature;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}
}

这样设计带来的问题是什么呢?

1)其他第三方公司接入气象站获取数据的问题
2)无法在运行时动态的添加第三方

也就是说我们想要再添加新的公告板的时候,同时需要更改WeatherData类,这样的话工作量变大,不符合设计要求。

根据上面观察者模式,我们重新设计我们的方案,也就是留出subject和observer两个接口,由WeatherDada和CurrentConditions分别继承实现,这样的话我再来新的公告板的话只需要扩展observer这个接口就可以了。

3

observer接口:

1
2
3
public interface Observer {
public void update(float mTemperatrue,float mPressure,float mHumidity);
}

subject接口:

1
2
3
4
5
6
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();

}

WeatherDataSt继承自subject,然后observer由ArrayList组成,这样的话每扩展一个公告板就只需要注册Observer就可以了。

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
public class WeatherDataSt implements Subject {
private float mTemperature;
private float mPressure;
private float mHumidityp;
ArrayList<Observer> observers;

public float getmTemperature() {
return mTemperature;
}

public float getmPressure() {
return mPressure;
}

public float getmHumidityp() {
return mHumidityp;
}

public WeatherDataSt(){
observers = new ArrayList<>();
}

@Override
public void registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
if (observers.contains(o)){
observers.remove(o);
}

}

@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++){
observers.get(i).update(getmTemperature(),getmPressure(),getmHumidityp());
}
}

public void dataChange(){
notifyObservers();
}

public void setData(float mTemperature,float mPressure,float mHumidity){
this.mTemperature = mTemperature;
this.mPressure = mPressure;
this.mHumidityp = mHumidity;
dataChange();
}
}

公告板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ConcurrentConditions implements Observer{

private float mTemperature;
private float mPressure;
private float mHumidityp;

@Override
public void update(float mTemperature, float mPressure, float mHumidity) {
this.mTemperature =mTemperature;
this.mPressure = mPressure;
this.mHumidityp = mHumidity;
display();
}

public void display(){
System.out.println("***Today mTemperatrue:" + mTemperature + "***");
System.out.println("***Today mPressure:" + mPressure + "***");
System.out.println("***Today mHumidity:" + mHumidityp + "***");
}
}

这个时候我想要添加一个气象预报的公告板,只需要继承Observer接口然后在WeatherData中注册就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ForcastConditions implements Observer{
private float mTemperature;
private float mPressure;
private float mHumidityp;
@Override
public void update(float mTemperature, float mPressure, float mHumidity) {
this.mTemperature = mTemperature;
this.mPressure = mPressure;
this.mHumidityp = mHumidity;
displsy();
}

public void displsy(){
System.out.println("**明天温度:"+(mTemperature+Math.random())+"**");
System.out.println("**明天气压:"+(mPressure+10*Math.random())+"**");
System.out.println("**明天湿度:"+(mHumidityp+Math.random())+"**");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class InternetWeatherOb {

public static void main(String[] args){
ConcurrentConditions concurrentConditions = new ConcurrentConditions();
WeatherDataSt weatherDataSt = new WeatherDataSt();
weatherDataSt.registerObserver(concurrentConditions);
weatherDataSt.setData(10,200,20);

ForcastConditions forcastConditions = new ForcastConditions();
weatherDataSt.registerObserver(forcastConditions);
weatherDataSt.setData(20,300,30);
}
}

通过上面的例子我们可以看出通过观察者模式,我们可以轻松的扩展程序,扩展后只需要让观察者通知被观察者发生的改变即可。

其实观察者模式有两种的实现方式,就是推和拉的模式,推的模式其实就是有主题去通知观察者那些状态发生了改变,拉的方式就是有观察者自己去获取自己需要的状态的改变,对于不需要的不需要获取。

3. java内置的观察者模式

Java内置的观察者:

  • Observable
  • Observer

接下来通过内置的观察者模式来上面的例子

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
public class WeatherData extends Observable{
private float mTemperatrue;
private float mPressure;
private float mHumidity;

public float getTemperature()
{
return mTemperatrue;
}

public float getPressure()
{
return mPressure;
}

public float getHumidity()
{
return mHumidity;
}
public void dataChange(){
this.setChanged(); //boolean类型的,是否发生了改变
this.notifyObservers(new Data(getTemperature(),getPressure(),getHumidity())); //通知观察者发生的改变
}

public void setData(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}

public class Data
{
public float mTemperatrue;
public float mPressure;
public float mHumidity;
public Data(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ConcurrentConditions implements Observer {

private float mTemperature;
private float mPressure;
private float mHumidityp;

@Override
public void update(Observable o, Object arg) {
this.mTemperature = ((WeatherData.Data) arg).mTemperatrue;
this.mPressure = ((WeatherData.Data) arg).mPressure;
this.mHumidityp = ((WeatherData.Data) arg).mHumidity;
display();
}
public void display()
{
System.out.println("***Today mTemperatrue:" +mTemperature+"***");
System.out.println("***Today mPressure:" +mPressure+"***");
System.out.println("***Today mHumidity:" +mHumidityp+"***");
}
}
1
2
3
4
5
6
7
8
public class InternetWeather {
public static void main(String[] args){
ConcurrentConditions concurrentConditions = new ConcurrentConditions();
WeatherData weatherData = new WeatherData();
weatherData.addObserver(concurrentConditions); //添加
weatherData.setData(10,100,10);
}
}

其实就是通过继承Java内部的Observable和Observer,实现观察者模式,不用自己去写这俩个接口了。

4. 典型应用

Observer模式的典型应用:

  • 侦听事件驱动程序设计中的外部事件
  • 侦听/监视某个对象的状态变化
  • 发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者

本文标题:设计模式之观察者模式

文章作者:WilsonSong

发布时间:2018年10月07日 - 15:10

最后更新:2018年10月12日 - 11:10

原始链接:https://songwell1024.github.io/2018/10/07/observer/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------