天天看點

觀察者模式的簡單使用

前言

我們在Android ListView或者Recyclerview的開發中,常常是資料更新之後要立刻去更新界面,這種Adapter的notifyDateSetChanged就運用了觀察者模式;觀察者模式是一種使用率非常高的模式,常常用到GUI之類的的系統中,界面跟随資料發生改變,使得UI和具體的頁面邏輯分開解耦,換而言之,觀察者模式就是一對多的關系,一個對象發生改變,其依賴于它的所有對象都收到通知,更新對象本身。

适用場景

1,事件多級觸發場景,一對多關系的一種展現。

2,事件總線場景,這個最多使用的是EventBus。

角色扮演

Subject:被觀察者的抽象類,一般提供接口滿足添加和删除觀察者對象。

ConcreteSubject:具體的被觀察者,實作Subject的接口。

Observer:抽象觀察者,就是觀察者的抽象類,定義更新接口來更新自己的資料。

ConcreteObserver:具體觀察者,實作更新自身資料的方法。

觀察者模式的簡單實作

簡單抽象一個demo,我的桌子上有一個電子溫度計,其功能就是顯示室内溫度跟濕度,那麼我們抽象的過程就是,當溫控原件檢測到溫度和濕度發生變化時,通知界面重新整理,首先我們申明抽象被觀察者:

package com.demo.observable;

/**
 * Created by italkbb on 2017/12/25.
 */

/**
 * 被觀察者的接口
 */
public interface Subject {

    /**
     * 注冊觀察者
     */
    void registerObserver(Observer observer);

    /**
     * 移除觀察者
     */
    void deleteObservers();

    /**
     * 通知觀察者
     */
    void notifyObservers();

}
           

下面實作這個被觀察者:

package com.demo.observable;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by italkbb on 2017/12/25.
 */

public class WeatherSubject implements Subject {
    // 觀察者
    private List<Observer> mObservers;
    // 資訊
    private float mTemperature;//溫度
    private float mHumidity;//濕度

    public WeatherSubject(){
        this.mObservers = new ArrayList<Observer>();
    }

    @Override
    public void registerObserver(Observer observer) {
        this.mObservers.add(observer);
    }

    @Override
    public void deleteObservers() {
        this.mObservers.clear();
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : mObservers) {
            observer.updateInfo();
        }
    }

    public void setWeatherInfo(float temperature, float humidity) {
        this.mTemperature = temperature;
        this.mHumidity = humidity;
        // 資訊更新完畢了,通知觀察者
        notifyObservers();
    }

    /**
     * 得到測量溫度
     * @return
     */
    public float getTemperature() {
        return mTemperature;
    }

    /**
     * 得到測量濕度
     * @return
     */
    public float getHumidity() {
        return mHumidity;
    }
}
           

上面的類基本滿足了裝置資訊改變通知觀察者的功能,下面同樣來寫觀察者:

package com.demo.observable;

/**
 * Created by italkbb on 2017/12/25.
 */

/**
 * 觀察者接口
 */
public interface Observer {
    void updateInfo();
}
           

實作它:

package com.demo.observable;

/**
 * Created by italkbb on 2017/12/25.
 */

public class WeatherInfoDisplay implements Observer {
    private WeatherSubject mWeatherSubject;

    private float mTemperature;//溫度
    private float mHumidity;//濕度

    public WeatherInfoDisplay(WeatherSubject weatherSubject){
        this.mWeatherSubject = weatherSubject;
        this.mWeatherSubject.registerObserver(this);
    }

    public void display() {
        // 簡單抽象顯示為java的列印語句
        System.out.println("溫度:" + this.mTemperature + "℃");
        System.out.println("濕度:" + this.mHumidity);
    }

    @Override
    public void updateInfo() {
        this.mTemperature = this.mWeatherSubject.getTemperature();
        this.mHumidity = this.mWeatherSubject.getHumidity();
        display();
    }
}
           

接下來就簡單使用一下:

package com.demo.observable;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import teltplay.example.com.kotlindemo.R;

public class WeatherActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_weather);
        WeatherSubject mWeatherSubject = new WeatherSubject();
        WeatherInfoDisplay mWeatherInfoDisplay = new WeatherInfoDisplay(mWeatherSubject);

        mWeatherSubject.setWeatherInfo(f,f);
    }
}
           

就這樣一個簡單的觀察者模式就完成了,當然要實作複雜的邏輯,也就大同小異了。

後記

優點缺點說一下,優點嘛,觀察者與被觀察者低耦合,也就增加了代碼的擴充性;缺點就是如果簡單的采取循環通知觀察者,可能會由于順序執行的關系卡住線程,但這個用多線程就會解決,但同時帶來的就是效率問題了,太多的觀察者,在多線程,資源消耗值得考慮。