觀察者模式詳解

本篇文章總結一下觀察者模式,主要從以上幾點介紹。 html

聲明

概念介紹

觀察模式是咱們在開發過程當中常常遇到的一種設計模式,這裏先來介紹一下概念。java

從字面意思上去理解,所謂的觀察者模式,首先有觀察者(一個或者多個),被觀察者(一個)。當被觀察者狀態發生變化的時候,就會去通知它的全部的觀察者,而後由觀察者根據被觀察者的狀況做出反應。觀察者模式屬於行爲型模式。android

在 Android 中的接口回調屬於一種特殊的觀察者模式,觀察者只有一個(監聽器)。設計模式

在觀察者模式中有如下四個重要的角色:異步

Subject 被觀察者的抽象接口,在這個接口裏面,提供添加觀察者和刪除觀察者、通知觀察者的方法。ide

ConcreteSubject 具體的被觀察者,Subject 的實現類。在具體的內部發生改變時,給全部註冊過的觀察者發送通知。佈局

Observer 觀察者的抽象接口,在這個接口裏面定義了一個更新方法,當收到被觀察者的通知的時候就更新。測試

ConcreteObserver 具體的觀察者,Observer 的實現類。實現 Observer 的更新方法。this

圖例解釋

使用送奶工的圖例來介紹:spa

送奶工就能夠看作是一個被觀察者,而後訂奶的張3、趙4、王五就是觀察者(觀察送奶工有沒有來),送奶工來送奶會通知各位觀察者,通知他們來取奶。這個時候某六也想訂奶,就能夠告訴送奶工,之後他也要訂奶了,之後來送奶的時候就也會通知某六了。

某一天,張三不想喝奶了,就告訴送奶工,他再也不訂奶了,那麼之後送奶工再來送奶就不會通知張三了。

具體的代碼實現

根據上面的概念,須要有一個被觀察者的抽象,也就是送奶工的抽象

public interface Subject{
    // 註冊觀察者
    void registerObserver(Observer o);
    // 取消註冊觀察者
    void unregisterObserver(Observer o);
    // 通知觀察者
    void notifyObserver();
}
複製代碼

具體的觀察者對象

public class ConcreteSubject implements Subject{
    // 存放觀察者的集合
    private List<Observer> observers;
    public ConcreteSubject(){
        observers = new ArrayList<>();
    }
    // 註冊觀察者
    @Override
    public void registerObserver(Observer o){
        if(!observers.contains(o)){
            observers.add(o);
        }
    }
    // 取消註冊觀察者
    @Override
    public void unregisterObserver(Observer o){
        if(observers.contains(o)){
            observers.remove(o);
        }
    }
    @Override
    public void notifyObserver(){
        for(int i=0;i<observers.size();i++){
            observers.get(i).update();
        }
    }
    
}
複製代碼

觀察者抽象接口

public interface Observer{
    void update();
}
複製代碼

觀察者具體的實現

public class ConcreteObserver implements Observer{
    private Subject subject;
    public ConcreteObserver(Subject subject){
        this.subject = subject;
        this.subject.registerObserver(this);
    }
    @Override
    public void update(){
        // 具體的實現
    }
}
複製代碼

到此關於觀察者和被觀察者都已經寫完了,這僅僅是最簡單的寫法,固然 update() 方法裏面能夠傳遞參數,具體的實現讓被觀察者來實現。這就具體狀況具體寫了。

下面寫 main 方法來測試

public static void main(String[] args){
    // 建立被觀察者
    Subject subject = new ConcreteSubject();
    // 建立觀察者
    Observer one = new ConcreteObserver(subject);
    // 建立觀察者
    Observer two = new ConcreteObserver(subject);
    // 建立觀察者
    Observer three = new ConcreteObserver(subject);
    // 狀態發生改變,通知觀察者
    subject.notifyObserver();
}
複製代碼

實際運用實例

在 Java 中實現觀察模式,須要藉助系統 API 提供的類,在包 java.util 中的 ObserverObservable 。被觀察者須要繼承 Observable 類,觀察者須要實現接口 Observer 而且實現其中的 update(Observable o,Object arg) 方法

其實雖然系統提供了 ObserverObservable ,經過看源碼你會發現它們的實現和咱們上面寫的例子是很像的,沒什麼難點。Observable 類裏面主要就是有存放 Observer 的集合和方法 addObserver(Observer o)deleteObserver(Observer o)notifyObservers(Object arg) 觀察者接口 Observer 內部主要有方法update(Observable o,Object arg)

簡單運用

房子的價格和咱們的生活息息相關,人們都很關注房價,假設房子是被觀察者,人們是觀察者,當房價發生變化的時候就會通知人們,這就就是一個觀察者模式,用 Java 代碼體現,下面列出核心代碼,具體代碼見連接

被觀察者

public class House extends Observable {
    private float house_price;

    public House(float house_price) {
        this.house_price = house_price;
    }

    public void updatePrice(float house_price) {
        this.house_price = house_price;
        // 設置變化點
        setChanged();
        // 通知觀察者
        notifyObservers(house_price);
    }

    @NonNull
    @Override
    public String toString() {
        return "當前房子價格:" + house_price;
    }
}
複製代碼

觀察者

public class People implements Observer {
    private String name;

    public People(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        Float price = (Float) arg;
        Log.e("update", "我是觀察者" + name + "房子價格變了:" + price);
        if (price > 20000) {
            Log.e("update:", "太TM貴啦,不關注了!");
            o.deleteObserver(this);
        }
    }
}
複製代碼

驗證:

public class DesignModeActivity extends BaseActivity {

    @BindView(R.id.bt_change)
    Button btChange;
    @BindView(R.id.et_price)
    EditText etPrice;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_observer);
        ButterKnife.bind(this);
        init();
    }

    @Override
    public void initView() {
        super.initView();
        // 房子 屬於被觀察者
        House house = new House(10000);
        // 建立觀察者
        People people = new People("張三");
        People people1 = new People("李四");
        People people2 = new People("王五");
        // 註冊觀察者
        house.addObserver(people);
        house.addObserver(people1);
        house.addObserver(people2);

        btChange.setOnClickListener(v -> {
            float price = Float.parseFloat(etPrice.getText().toString());
            house.updatePrice(price);
        });

    }
}

複製代碼

顯示結果:

重點: 須要將觀察者一一添加到被觀察者中,當被觀察者對象內容發生變化的時候調用 setChanged()notifyObservers() 來通知觀察者

Android 中的觀察者

在 Android 中常常用到觀察者模式,最經常使用的就是設計點擊監聽事件(這是一種特殊的觀察者模式,觀察者只有一個)、ContentObserver、android.database.Observable 等;還有組件通信庫RxJavaRxAndroidEventBus 等。這裏拿 Adapter 中的觀察者模式來舉例:

先來看一下 BaseAdapter 的部分源碼

public abstract class BaseAdapter implements ListAdapter,SpinnerAdapter{
    // 數據是否變化的被觀察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
    public boolean hasStablelds(){
        return false;
    }
    public void registerDataSetObserver(DataSetObserver observer){
        // 其實就是 mDataSetObservable 內部有一個 List 用來保存觀察者
        mDataSetObservable.registerObserver(observer);
    }
    public void unregisterDataSetObserver(DataSetObserver observer){
        mDataSetObservable.unregisterObserver(observer);
    }
    // 當數據集合發生變化的時候,通知全部的觀察者
    public void notifyDataSetChanged(){
        mDataSetObservable.notifyChanged();
    }
    
}
複製代碼

從源碼中能夠很容易的看到,這裏有 被觀察者對象 mDataSetObservable 當數據發生變化的時候,咱們就調用 notifyDataSetChanged() 方法,這個時候 被觀察者對象 就會通知觀察者做出反應。就是這麼一個過程!

整個過程咱們都瞭解了,下面就是進一步看看代碼是怎麼實現的:看看 mDataSetObservable.notifyChanged() 作了什麼

public class DataSetObservable extends Observable<DataSetObserver>{
    // 這個方法就是挨個通知觀察者,其中儲存觀察者的 List 在 Observable 中,包括 registerObserver 和 unregisterObserver 方法
    public void notifyChanged(){
        synchronized(mObservers){
            for(int i = mObservers.size-1;i>=0;i--){
                mObservers.get(i).onChanged();
            }
        }
    }
}
複製代碼

這裏的 mObservers 就是觀察者的集合,這些觀察者是在 ListView 經過 setAdapter() 設置 Adapter 時產生的:

@Override
public void setAdapter(ListAdapter adapter){
    if(mAdapter!= null && mDataSetObserver != null){
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    // .... 省略代碼
    super.setAdapter(adapter);
    
    if(mAdapter != null){
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();
        //建立數據觀察者
        mDataSetObserver = new AdapterDataSetObserver();
        //註冊觀察者
        mAdapter.registerDataSetObserver(mDataSetObserver);
        //... 省略代碼
    }
       
}
複製代碼

再來看看觀察者 AdapterDataSetObserver 的部分關鍵代碼

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver{
    // 這個方法就是觀察者在收到通知後,調用的方法
    @Override
    public void onChanged(){
        super.onChanged();
        if(mFastScroller != null){
            mFastScroller.onSectionsChanged();
        }
    }
    @Override
    public void onInvalidated(){
        super.onInvalidated();
        if(mFastScroller != null){
            mFastScroller.onSectionsChanged();
        }
    }
}
複製代碼

上面代碼的主要實現部分都在它的父類,再來看看 AdapterDataSetObserver 的父類 AdapterView 的 AdapterDataSetObserver :

class AdapterDataSetObserver extends DataSetObserver{
    private Parcelable mInstanceState = null;
    @Override
    public void onChanged(){
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();
        if(AdapterView.this.getAdapter().hasStableIds()&& mInstanceState != null && mOldItemCount==0 && mItemCount > 0){
            AdapterView.this.onRestoreInstanceState(mInstanceState);
            mInstanceState = null;
        }else{
            rememberSyncState();
        }
        checkFocus();
        // 從新佈局
        requestLayout();
    }
    // .... 省略代碼
    
    public void clearSavedState(){
        mInstanceState = null;
    }
}
複製代碼

在源碼中能夠看到在 onChanged() 方法中調用了 requestLayout() 方法來從新進行佈局。到此 BaseAdapter 的觀察模式就縷清楚了。

觀察者模式的使用場景和優缺點

使用場景

  • 事件多級觸發場景
  • 跨系統的消息交換場景,如消息隊列、事件總線的處理機制

優勢

解除耦合,讓耦合的雙方都依賴於抽象,從而使得各自的變換都不會影響到另外一邊的變換

缺點

在應用觀察者模式時須要考慮一下開發效率和運行效率的問題,程序中包括一個被觀察者,多個觀察者,開發、調試等內容會比較複雜,並且在 Java 中消息的通知通常是順序執行的,那麼一個觀察者卡頓,就會影響總體的執行效率,在這種狀況下,通常會採用異步實現。

總結:

其實不管程序總體多複雜,觀察者模式是永遠不會變的,首先有一個被觀察者,被觀察者對象中持有觀察者,當被觀察者發生變化的時候,調用被觀察者的方法通知觀察者(這個方法內一般會是 觀察者調用本身對應的方法)。

至於觀察者何時被添加到被觀察者中,觀察者收到信號後若是處理,這就是具體狀況具體處理了。

參考內容 blog.csdn.net/itachi85/ar…> www.jianshu.com/p/9ee823cd9… www.2cto.com/kf/201310/2…

相關文章
相關標籤/搜索