Android設計模式之——觀察者模式

一、介紹

觀察者模式是一個使用率非常高的模式,它最常用的地方是GUI系統、訂閱——發布系統。因為這個模式的一個重要作用就是解耦,將被觀察者和觀察者解耦,使得它們之間的依賴性更小,甚至做到毫無依賴。以GUI系統來說,應用的UI具有易變性,尤其是前期隨著業務的改變或者產品的需求修改,應用界面也會經常性變化,但是業務邏輯基本變化不大,此時,GUI系統需要一套機制來應對這種情況,使得UI層與具體的業務邏輯解耦,觀察者模式此時就派上用場了。

二、定義

定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所有依賴于它的對象都會得到通知并被自動更新。

三、使用場景

  • 關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是”組合“關系。

  • 事件多級觸發場景。

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

四、觀察者模式的UML類圖

UML類圖:

這里寫圖片描述

角色介紹:

  • Subject:抽象主題,也就是被觀察者(Observable)的角色,抽象主題角色把所有觀察者對象的引用保存到一個聚集里,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。

  • ConcreteSubject:具體主題,該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給所有注冊過的觀察者發出通知,具體主題角色又叫做具體被觀察者(ConcreteObservable)角色。

  • Observer:抽象觀察者,該角色是觀察者的抽象類,它定義了一個更新接口,使得在得到主題的更改通知時更新自己。

  • ConcreteObserver:具體的觀察者,該角色實現抽象觀察者角色所定義的更新接口,以便主題的狀態發生改變化時更新自身的狀態。

五、簡單實現

這里舉一個追劇的例子,平常為了不錯過最新的電視劇我們會訂閱或關注這個電視劇,當電視劇更新后會第一時間推送給我們,下來就簡單實現一下。

抽象觀察者類:

/***  抽象觀察者類,為所有具體觀察者定義一個接口,在得到通知時更新自己*/
public interface Observer {/***  有更新*  *  @param message 消息*/public void update(String message);}

抽象被觀察者類:

/*** 抽象被觀察者類*/
public interface Observable {/*** 推送消息* * @param message 內容*/void push(String message);/*** 訂閱* * @param observer 訂閱者*/void register(Observer observer);
}

具體的觀察者類:

/*** 具體的觀察者類,也就是訂閱者*/
public class User implements Observer {@Overridepublic void update(String message) {System.out.println(name + "," + message + "更新了!");}// 訂閱者的名字private String name;public User(String name) {this.name = name;}
}

具體的被觀察者類:

/***  具體的被觀察者類,也就是訂閱的節目*/
public class Teleplay implements Observable{private List<Observer> list = new ArrayList<Observer>();//儲存訂閱者@Overridepublic void push(String message) {for(Observer observer:list){observer.update(message);}}@Overridepublic void register(Observer observer) {list.add(observer);}}

實現:

public class Client {public static void main(String[] args) {//被觀察者,這里就是用戶訂閱的電視劇Teleplay teleplay = new Teleplay();//觀察者,這里就是訂閱用戶User user1 = new User("小明");User user2 = new User("小光");User user3 = new User("小蘭");//訂閱teleplay.register(user1);teleplay.register(user2);teleplay.register(user3);//推送新消息teleplay.push("xxx電視劇");}
}

結果:

小明,xxx電視劇更新了!
小光,xxx電視劇更新了!
小蘭,xxx電視劇更新了!

由上面的代碼可以看出實現了一對多的消息推送,推送消息都是依賴Observer和Observable這些抽象類,而User和Teleplay完全沒有耦合,保證了訂閱系統的靈活性和可擴展性。

六、Android源碼中的觀察者模式

1、BaseAdapter

BaseAdapter我相信大家都不陌生,在ListView的適配器中我們都是繼承它。下面來簡單分析分析。

BaseAdapter 部分代碼:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {//數據集觀察者private final DataSetObservable mDataSetObservable = new DataSetObservable();public boolean hasStableIds() {return false;}public void registerDataSetObserver(DataSetObserver observer) {mDataSetObservable.registerObserver(observer);}public void unregisterDataSetObserver(DataSetObserver observer) {mDataSetObservable.unregisterObserver(observer);}/*** 當數據集變化時,通知所有觀察者*/public void notifyDataSetChanged() {mDataSetObservable.notifyChanged();}
}

看看mDataSetObservable.notifyChanged()方法:

public class DataSetObservable extends Observable<DataSetObserver> {/*** Invokes {@link DataSetObserver#onChanged} on each observer.* Called when the contents of the data set have changed.  The recipient* will obtain the new contents the next time it queries the data set.*/public void notifyChanged() {synchronized(mObservers) {// since onChanged() is implemented by the app, it could do anything, including// removing itself from {@link mObservers} - and that could cause problems if// an iterator is used on the ArrayList {@link mObservers}.// to avoid such problems, just march thru the list in the reverse order.for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged();}}}
}

可以看出在mDataSetObservable.notifyChanged()中遍歷所有觀察者,并調用他們的onChanged(),從而告知觀察者發生了什么。

那么觀察者怎么來的,那就是setAdapter方法,代碼如下:

    @Overridepublic void setAdapter(ListAdapter adapter) {if (mAdapter != null && mDataSetObserver != null) {mAdapter.unregisterDataSetObserver(mDataSetObserver);}resetList();mRecycler.clear();if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);} else {mAdapter = adapter;}mOldSelectedPosition = INVALID_POSITION;mOldSelectedRowId = INVALID_ROW_ID;// AbsListView#setAdapter will update choice mode states.super.setAdapter(adapter);if (mAdapter != null) {mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();mOldItemCount = mItemCount;mItemCount = mAdapter.getCount();checkFocus();mDataSetObserver = new AdapterDataSetObserver();mAdapter.registerDataSetObserver(mDataSetObserver);//注冊觀察者......省略}}

AdapterDataSetObserver定義在ListView的父類AbsListView中,是一個數據集觀察者,代碼:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {@Overridepublic void onChanged() {super.onChanged();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}@Overridepublic void onInvalidated() {super.onInvalidated();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}
}

它由繼承自AbsListView的父類AdapterView的AdapterDataSetObserver, 代碼如下 :

class AdapterDataSetObserver extends DataSetObserver {private Parcelable mInstanceState = null;// 上文有說道,調用Adapter的notifyDataSetChanged的時候會調用所有觀察者的onChanged方法,核心實現就在這里@Overridepublic void onChanged() {mDataChanged = true;mOldItemCount = mItemCount;// 獲取Adapter中數據的數量mItemCount = getAdapter().getCount();// Detect the case where a cursor that was previously invalidated has// been repopulated with new data.if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null&& mOldItemCount == 0 && mItemCount > 0) {AdapterView.this.onRestoreInstanceState(mInstanceState);mInstanceState = null;} else {rememberSyncState();}checkFocus();// 重新布局ListView、GridView等AdapterView組件requestLayout();}// 代碼省略public void clearSavedState() {mInstanceState = null;}
}

當ListView的數據發生變化時,調用Adapter的notifyDataSetChanged函數,這個函數又會調用DataSetObservable的notifyChanged函數,這個函數會調用所有觀察者 (AdapterDataSetObserver) 的onChanged方法。這就是一個觀察者模式!

七、總結

優點:

  • 觀察者和被觀察者之間是抽象耦合,應對業務變化。

  • 增強系統的靈活性和可擴展性。

缺點:

  • 在應用觀察者模式時需要考慮一下開發效率和運行效率的問題,程序中包括一個被觀察者、多個觀察者,開發、調試等內容會比較復雜,而且在Java中消息的通知一般是順序執行,那么一個觀察者卡頓,會影響整體的執行效率,在這種情況下,一般會采用異步實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447010.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android設計模式之——備忘錄模式

一、介紹 備忘錄模式是一種行為模式&#xff0c;該模式用于保存對象當前狀態&#xff0c;并且在之后可以再次恢復到此狀態&#xff0c;這有點像我們平時說的”后悔藥“。備忘錄模式實現的方式需要保證被保存的對象狀態不能被對象從外部訪問&#xff0c;目的是為了保護好被保存…

c++ memory 頭文件詳細介紹

類 指針特征 pointer_traits (C11) 提供關于指針式類型的信息 (類模板) 垃圾收集器支持 pointer_safety (C11) 列出指針安全模式 (枚舉) 分配器 allocator 默認的分配器 (類模板) allocator_traits (C11) 提供關于分配器類型的信息 (類模板) allocator_arg_t (C11) 標簽類型…

C++ using的三種使用策略以及具體的用法

Using的使用方法 1&#xff0c;命名空間的使用 為了防止代碼沖突&#xff0c;都會使用到命名空間。假設這樣一種情況&#xff0c;當一個班上有兩個名叫 Zara 的學生時&#xff0c;為了明確區分他們&#xff0c;我們在使用名字之外&#xff0c;不得不使用一些額外的信息&#…

Android設計模式之——迭代器模式

一、介紹 迭代器模式&#xff08;Iterator Pattern&#xff09;又稱為游標&#xff08;Cursor&#xff09;模式&#xff0c;是行為型設計模式之一。迭代器模式算是一個比較古老的設計模式&#xff0c;其源于對容器的訪問&#xff0c;比如Java中的List、Map、數組等&#xff0c…

Android設計模式之——模板方法模式

一、介紹 在面向對象開發過程中&#xff0c;通常會遇到這樣的一個問題&#xff0c;我們知道一個算法所需的關鍵步驟&#xff0c;并確定了這些步驟的執行順序&#xff0c;但是&#xff0c;某些步驟的具體實現是未知的&#xff0c;或者說某些步驟的實現是會隨著環境的變化而改變…

Android設計模式之——訪問者模式

一、介紹 訪問者模式是一種將數據操作與數據結構分離的設計模式&#xff0c;它是《設計模式》中23種設計模式中最復雜的一個&#xff0c;但它的使用頻率并不高&#xff0c;正如《設計模式》的作者GOF對訪問者模式的描述&#xff1a;大多數情況下&#xff0c;你不需要使用訪問者…

C++類模板template <class T>簡單使用方法

一個簡單的例子 兩個數比大小 如果兩個數都是int類型 class Compare_int { public :Compare(int a,int b){xa;yb;}int max( ){return (x>y)?x:y;}int min( ){return (x<y)?x:y;} private :int x,y; }; 如果兩個數是float類型 class Compare_float { public :Compare(…

Android設計模式之——中介者模式

一、介紹 中介者模式&#xff08;Mediator Pattern&#xff09;也稱為調解者模式或調停者模式&#xff0c;Mediator本身就有調停者和調解者的意思。 在日常生活中調停者或調解者這個角色我們見得比較多的是“和事老”&#xff0c;也就是說調解兩個有爭端的人的角色&#xff0…

C++智能指針中unique_ptr部分內容的講解

參考鏈接 std::unique_ptr 介紹 定義位于頭文件<memory>std::unique_ptr 是通過指針占有并管理另一對象&#xff0c;并在 unique_ptr 離開作用域時釋放該對象的智能指針。 在下列兩者之一發生時用關聯的刪除器釋放對象&#xff1a;1&#xff0c;銷毀了管理的 unique_pt…

Java基礎——Java多線程中sleep()、wait()和notify()

一、sleep()sleep()方法源碼&#xff1a;/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does …

Key_handle的學習

代碼 一切盡在不言中 #pragma once#include "common/common.h" #include "sdf/sdf.h"#include <memory>namespace sdf {namespace algorithm {class KeyHandle {public:using erased_internal_data_t char; //使用erased_internal_data_t等效于ch…

Java基礎——虛擬機結構

一、Java平臺結構圖二、JVM、JRE和JDK關系JVM&#xff1a;Java Virtual Machine&#xff08;Java虛擬機&#xff09;&#xff0c;負責執行符合規范的Class文件 JRE&#xff1a; Java Runtime Environment &#xff08;java運行環境&#xff09;&#xff0c;包含JVM和類庫 JDK&a…

解決 SSH Connection closed by foreign host 問題

用 Xshell 連接服務器總是報錯 : Connection closed by foreign host.Disconnected from remote host... 原因可能是 SSH 服務器沒設置保活時間間隔 , 具體設置如下 : 操作 # vim /etc/ssh/sshd_config 添加兩行 , 或去掉注釋 : ClientAliveInterval 60ClientAliveCountMax…

Java基礎——synchronized

synchronized重要&#xff01;重要&#xff01;重要&#xff01;重要的事情說三遍&#xff0c;一定要記下來哦。 Java語言的關鍵字&#xff0c;當它用來修飾一個方法或者一個代碼塊的時候&#xff0c;能夠保證在同一時刻最多只有一個線程執行該段代碼。一、當兩個并發線程訪問同…

C++:MAC安裝Boost庫文件并且使用CLion開發

boost的filestem庫 C在17版本的標準庫中引入了一個filesystem庫&#xff0c;用來處理文件路徑&#xff0c;以及文件訪問。很多編譯器對filesystem庫的支持還不是很好。為了解決這個問題&#xff0c;可以臨時使用boost::filesystem來替代。其實C17標準中的filesystem庫就是從bo…

Java基礎——Java異常處理機制

一、引言 try…catch…finally恐怕是大家再熟悉不過的語句了&#xff0c;而且感覺用起來也是很簡單&#xff0c;邏輯上似乎也是很容易理解。不過&#xff0c;我親自體驗的“教訓”告訴我&#xff0c;這個東西可不是想象中的那么簡單、聽話。不信&#xff1f;那你看看下面的代碼…

clion在使用sqlite3的時候,顯示Undefined symbols for architecture x86_64錯誤的解決辦法

顯示Undefined symbols for architecture x86_64錯誤的原因 1、缺少靜態庫 環境&#xff1a;在模擬器上報錯但在真機上能運行成功&#xff0c;而且報的錯誤來自于第三方庫。原因&#xff1a;architecture x86_64 是指模擬器的架構&#xff0c;意思就是 Crypto 變量在模擬器架…

Java基礎——Java反射機制及IoC原理

一、概念 主要是指程序可以訪問&#xff0c;檢測和修改它本身狀態或行為的一種能力&#xff0c;并能根據自身行為的狀態和結果&#xff0c;調整或修改應用所描述行為的狀態和相關的語義。在java中&#xff0c;只要給定類的名字&#xff0c; 那么就可以通過反射機制來獲得類的所…

Ubuntu boost庫文件安裝編譯

簡單介紹 Boost庫是為C語言標準庫提供擴展的一些C程序庫的總稱&#xff0c;由Boost社區組織開發、維護.Boost向來有準標準庫之稱&#xff0c;很多新特性例如智能指針等都是先在boost中實現&#xff0c;后來被吸收到標準庫之中. Boost實現了日志、算法、日期、地理、數學、線程…

Java基礎——類加載機制及原理

一、什么是類的加載&#xff1f; 類的加載指的是將類的.class文件中的二進制數據讀入到內存中&#xff0c;將其放在運行時數據區的方法區內&#xff0c;然后在堆區創建一個java.lang.Class對象&#xff0c;用來封裝類在方法區內的數據結構。類的加載的最終產品是位于堆區中的Cl…