本篇文章總結一下觀察者模式,主要從以上幾點介紹。 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
中的 Observer
和 Observable
。被觀察者須要繼承 Observable 類,觀察者須要實現接口 Observer
而且實現其中的 update(Observable o,Object arg)
方法
其實雖然系統提供了 Observer
和 Observable
,經過看源碼你會發現它們的實現和咱們上面寫的例子是很像的,沒什麼難點。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 中常常用到觀察者模式,最經常使用的就是設計點擊監聽事件(這是一種特殊的觀察者模式,觀察者只有一個)、ContentObserver、android.database.Observable 等;還有組件通信庫RxJava
、RxAndroid
、EventBus
等。這裏拿 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…