Android Tech And Thoughts.

观察者模式

Word count: 1.9kReading time: 8 min
2019/12/12 Share

观察者模式(JDK中运用最多的模式之一) - 让你的对象知悉现状
TODO:观察者模式是不是也利用了策略模式的因素

实例分析:
WeatherDate 对象负责追踪目前的天气状况(温度、湿度、气压),我们希望能建立一个应用,有三种布告板,分别显示目前的状况·气象统计及简单的预报。当 WeatherObject 对象获得最新的测量数据时,三种布告板必须实时更新。
另外,我们希望能提供一些API,以供客户实现自己的布告板。

此系统的三个部分:

  • 气象站(获取数据的传感器)
  • WeatherData对象(追踪数据)
  • 布告板(显示天气状况给用户看)
1
public class WeatherData{
2
	public double getTempertura(){}
3
	public double getHumidity(){}
4
	// 一旦气象测量更新,此方法会被调用
5
	public void measurementsChanged(){}
6
}

我们先来看一个错误的示范:

1
public class WeatherData{
2
	// 实例变量声明
3
	
4
	public void measurementsChanged(){
5
		float temp = getTemperature();
6
		float humidity = getHumidity();
7
		float pressure = getPressure();
8
		
9
		currentConditionDisplay.update(temp,humidity,pressure);
10
		statisticsDisplay,update(temp,humidity,pressure);
11
		forecastDisplay.update(temp,humidity,pressure);
12
	}
13
}

上面的实现有两个问题
第一是针对具体实现编程,会导致我们以后增加布告板(客户实现的)时,必须修改程序
第二,update函数很统一,更像是一个接口,这里大量地重复了代码

认识观察者模式

只要联想下报纸和杂志地订阅,就能很容易地理解观察者模式
出版者(Subject)+订阅者 (Observer)= 观察者模式

仔细思考前面的实现,你会发现,如果要批量地通知这些布告板,就必须将他们统一起来(而不是一个一个地添加,一个个地处理),他们地共性就是都是一个 “订阅者”,我们可以用 List<订阅者> 来组织,很棒!

定义:

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

稍后你会看到,实现观察者模式地方法不止一种,但是以包含 Subject 与 Observer 接口地类设计的做法最为常见

Subject接口:

1
void registerObserver();
2
void removeObserver();
3
void notifyObservers();

Observer接口

1
void update();

设计原则:松耦合

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。比如,主题只知道观察者实现了某个接口(也就是Observer接口),但是主题不需要知道观察者的具体类是谁、做了些什么或其它细节。

我们可以在任何时候都增加观察者,因为主题唯一依赖的东西是一个实现了 Observer 接口的对象列表,所以我们可以在运行时,随时增加观察者。

实现Observer模式

1
public interface IObserver {
2
    void update(float temp,float humidity);
3
}
4
5
public interface ISubject {
6
    List<IObserver> observers=new ArrayList<>();
7
    void registerObserver(IObserver observer);
8
    void removeObserver(IObserver observer);
9
    void notifyData();
10
}

Subject | WeatherData.java

1
public class WeatherData implements ISubject {
2
    private String[] data = new String[]{"Hello","yudan","world"};
3
4
    // 从这里貌似可以看出,事件发送应该与事件接收不在同一个线程
5
    public void run(){
6
        new Thread(new Runnable() {
7
            @Override
8
            public void run() {
9
                while(true){
10
                    try {
11
                        Thread.sleep(1000);
12
                        notifyData();
13
                    } catch (InterruptedException e) {
14
                        e.printStackTrace();
15
                    }
16
                }
17
            }
18
        }).start();
19
20
    }
21
22
    @Override
23
    public void registerObserver(IObserver observer) {
24
        observers.add(observer);
25
    }
26
27
    @Override
28
    public void removeObserver(IObserver observer) {
29
        observers.remove(observer);
30
    }
31
32
    @Override
33
    public void notifyData() {
34
        for(IObserver observer:observers){
35
            int index = ((int)(Math.random()*1000))%3;
36
            observer.update(25,60);
37
        }
38
    }
39
}

Observer | MainActivity.java

1
public class MainActivity extends AppCompatActivity implements IObserver,DisplayElement {
2
    private static String TAG = "dan";
3
    private WeatherData weatherData;
4
5
    private float tempe;
6
    private float humidity;
7
8
    private TextView msgTv;
9
    private StringBuilder msg;
10
11
    @Override
12
    protected void onCreate(Bundle savedInstanceState) {
13
        super.onCreate(savedInstanceState);
14
        setContentView(R.layout.activity_main);
15
16
        msgTv = findViewById(R.id.message_tv);
17
        weatherData = new WeatherData();
18
19
        msg = new StringBuilder("");
20
        weatherData.run();
21
        weatherData.registerObserver(this);
22
    }
23
24
    @Override
25
    public void display() {
26
        msg.append("temp is :"+tempe+" , humidity is :"+humidity+"\n");
27
        runOnUiThread(new Runnable() {
28
            @Override
29
            public void run() {
30
                msgTv.setText(msg);
31
            }
32
        });
33
    }
34
35
    @Override
36
    public void update(float tempe,float humidity) {
37
        Log.d(TAG, "update: "+tempe+","+humidity);
38
        this.tempe = tempe;
39
        this.humidity = humidity;
40
41
        display();
42
    }
43
}

以上就是我们自己实现的一个完整的 Observer模式。你可以思考下它有什么缺陷
数据的更新只能通过 Subject push到Observer,有时候我们可能也需要 Observer 可以从 Subject 拉取数据

JDK中自带的 Observer 模式

在 JDK 中,Observerable (Subject) 是一个实体类(非接口),Observer 仍是一个接口,关于 Observable为什么设计成一个类而不是接口.StatckOverzflow上有相关的争论,有的认为是因为内部的 Flag(private的boolean变量) ,有的认为这是一个”mistake”,也就是说设计的缺陷,设计成Observable类,由于Java的单继承模式,导致已经扩展的类不能再继承 Observable,这时候可能就得像前面我们所讲的,自己设计一个Observable借接口了。

老规矩,先上源码

1
package java.util;
2
3
public interface Observer {
4
		// var2就是需要更新的数据,当然这里只适合更新一个数据,不过可以将多个数据封装起来
5
		// 这里为什么要传一个 Observable 我还是不太清楚
6
    void update(Observable var1, Object var2);
7
}
8
9
-----------------------------------------------------------------------
10
package java.util;
11
12
public class Observable {
13
		// 这个 changed 比较关键,代表数据有没有刷新,如果数据刷新了,changed应该被置为 true
14
		// 当 changed 为false时,Observable是不会响应 notifyObservers 方法的
15
    private boolean changed = false;
16
    private Vector<Observer> obs = new Vector();
17
18
    public Observable() {
19
    }
20
21
    public synchronized void addObserver(Observer var1) {
22
        if (var1 == null) {
23
            throw new NullPointerException();
24
        } else {
25
       			// 这里可以看出,是不能重复添加同一个 Observer 的
26
            if (!this.obs.contains(var1)) {
27
                this.obs.addElement(var1);
28
            }
29
30
        }
31
    }
32
33
    public synchronized void deleteObserver(Observer var1) {
34
        this.obs.removeElement(var1);
35
    }
36
37
    public void notifyObservers() {
38
        this.notifyObservers((Object)null);
39
    }
40
41
    public void notifyObservers(Object var1) {
42
        Object[] var2;
43
        synchronized(this) {
44
            if (!this.changed) {
45
                return;
46
            }
47
48
            var2 = this.obs.toArray();
49
            this.clearChanged();
50
        }
51
52
				// 这里是逆序输出的(但是继承类可以重写此方法),我们不应该依赖于注册的顺序而推断出接收的顺序
53
				// 不依赖于次序,则是松耦合的而具体体现
54
        for(int var3 = var2.length - 1; var3 >= 0; --var3) {
55
            ((Observer)var2[var3]).update(this, var1);
56
        }
57
58
    }
59
60
    public synchronized void deleteObservers() {
61
        this.obs.removeAllElements();
62
    }
63
64
//------------------protected method------------------------
65
		// changed置为true
66
    protected synchronized void setChanged() {
67
        this.changed = true;
68
    }
69
70
    protected synchronized void clearChanged() {
71
        this.changed = false;
72
    }
73
//---------------------------------------------------------
74
75
    public synchronized boolean hasChanged() {
76
        return this.changed;
77
    }
78
79
    public synchronized int countObservers() {
80
        return this.obs.size();
81
    }
82
}

我们看到,和我们之前不一样的地方主要有两点:

  1. setChanged 方法
  2. Observale提供了get方法
  3. update回调里面传递了Observable对象(以便使用get方法)

用JDK自带的Observale接口来实现WeatherData项目:

1
public class WeatherData extends Observable {
2
3
    private float tempe;
4
    private float humidity;
5
    private float pressure;
6
7
    public WeatherData(){}
8
9
    public void measurementChanged(){
10
        //这里调用了 setChanged 方法,表示数据更新了
11
        setChanged();
12
        // 这里面会回调update(Observale,var1)方法
13
        notifyObservers();
14
    }
15
16
    public void setMeasurements(float tempe,float humidity,float pressure){
17
        this.tempe = tempe;
18
        this.humidity = humidity;
19
        this.pressure = pressure;
20
        measurementChanged();
21
    }
22
23
    public float getTempe(){
24
        return tempe;
25
    }
26
27
    public float getHumidity(){
28
        return humidity;
29
    }
30
31
    public float getPressure(){
32
        return pressure;
33
    }
34
}

基本和我们之前的相同

Thanks:

why is Observable not an abstract class or interface
Head First Design Pattern

CATALOG
  1. 1. 认识观察者模式
  2. 2. 实现Observer模式
  3. 3. JDK中自带的 Observer 模式
  4. 4. Thanks: