觀察者模式是使用最為頻繁的設計模式之一。在很多地方都有用到。比如各種編程語言的GUI事件處理實現(xiàn),各種框架的實現(xiàn),比如說EventBus、Rxjava以及MVC等等。
一、初次相識
先舉個小栗子來了解一下觀察者是干啥的~~
當我們在打團隊游戲時,當你受到攻擊需要隊友幫忙時該怎么辦?
這時候就需要給你所有的隊友發(fā)送一條你正在被攻擊的消息。所有的隊友會根據(jù)你發(fā)送的消息作出相應的動作。比如有團隊意識來幫你,或者不幫你繼續(xù)玩自己的。
這里面的隊員就是該設計模式名字中的觀察者。那么受到攻擊的自己的是什么呢。被觀察者?不,準確的我們稱之為目標或者主題。
所以整個流程大概就是:當目標(主題)的狀態(tài)發(fā)送改變時就會通知觀察者,觀察者根據(jù)自己的情況做出相應的動作。
二、進步熟悉
通過上面我們大概對觀察者的作用有了淺層的認識,這對接下來的學習很有幫助。
1.首先來看一下觀察者模式(Observe Pattern)的正經(jīng)定義:定義對象之間的一對多依賴關系,使得每當一個對象狀態(tài)發(fā)生改變時,其相關依賴對象皆得到通知并被自動更新。
它還有幾個別名:發(fā)布-訂閱模式、模型-視圖模式、源-監(jiān)聽器模式以及從屬者模式。比如我們看這個發(fā)布-訂閱模式就很好理解,當訂閱者訂閱了某系列雜志,當雜志有了新的狀態(tài),比如更新了,那么此時就會給所有的訂閱者發(fā)送一條消息,那么所有的訂閱者就會收到此消息做出購買或不購買的選擇。
2.那么它如何實現(xiàn)這么神奇的功能的呢?
先來看一下它的結構圖
從圖中我們可以看到之前說的目標和觀察者。只不過這里的目標和觀察者都被抽象化了。我們來逐一認識:
(1)Subject(目標或主題)
它是指被觀察的對象。我們在主題中定義一個觀察者集合。一個觀察者對象可以接收任意多個觀察者。同時提供了一系列的方法管理這些觀察者。
比如attach添加觀察者到集合中,detach從集合中剔除觀察者。notify通知集合中的所有觀察者。
(2)ConcreteSubject(具體目標)
它擁有自己的狀態(tài),當它的狀態(tài)的改變時就會通知各個觀察者。
同時還實現(xiàn)了在目標類中定義的抽象邏輯方法(如果有的話)
(3)Observer(抽象觀察者)
它是一個接口,觀察者將對觀察目標狀態(tài)的改變做出相應的反應
該接口定義了更新數(shù)據(jù)的方法update
(4)ConcreteObserver(具體觀察者)
具體觀察者中會維護一個指向具體目標對象的引用,它存儲了具體觀察者的狀態(tài),這些狀態(tài)和具體目標的狀態(tài)要保持一致。
它實現(xiàn)了抽象觀察者對象的updata方法。
通常在實現(xiàn)時,可以調(diào)用具體目標的attach和detach方法將自己加入到集合中或者從集合中剔除。
3.接下來通過一個代碼實例來加深對上面的結構圖的理解
目標抽象類 Subject.java
public abstract class Subject {? ? ?
protected ArrayList observers=new ArrayList<>();? ? ?
//把觀察者對象添加到觀察者集合中? ? ?
public void attach(Observer observer) {? ? ? ? ? ?
observers.add(observer);? ? ?
}? ? ?
//把觀察者對象剔除到觀察者集合中? ? ?
public void detach(Observer observer) {? ? ? ? ? ?
observers.remove(observer);? ? ?
}? ? ? ? ? ? ? ? ? ? ? ? ?
//聲明抽象方法? ? ?
public abstract void notifyObserver();}
具體目標類 ConcreteSubject.java 繼承抽象類并實現(xiàn)通知觀察者的方法
public class ConcreteSubject extends Subject{? ? ?
//實現(xiàn)通知的方法? ? ?
@Override? ? ?
public void notifyObserver() {? ? ? ? ? ?
System.out.println("目標對象狀態(tài)已變化......發(fā)送通知給觀察者中");? ? ? ? ? ?
for(Object object:observers){? ? ? ? ? ? ? ? ? ?
((Observer)object).update();? ? ? ? ? ?
}? ? ? ? ? ? ? ? ?
}}
觀察者接口 Observer 定義一個更新的方法
//觀察者接口
public interface Observer {? ? ?
public void update();
}
具體的觀察者 ConcreteSubject.java
public class ConcreteObserver implements Observer {? ? ?
private String observerName;? ? ?
public ConcreteObserver(String observerName) {? ? ? ? ? ?
this.observerName=observerName;? ? ?
}? ? ?
@Override? ? ?
public void update() {? ? ? ? ? ?
System.out.println(observerName+"我要更新一下我的狀態(tài)了......");? ? ? }
}
這里再添加一個具體的觀察者,測試是否能夠同時收到消息并更新狀態(tài)ConcreteOberverOther.java
public class ConcreteOberverOther implements Observer{? ? ?
private String observerName;? ? ? ? ? ? ?
public ConcreteOberverOther(String observerName) {? ? ? ? ? ?
this.observerName=observerName;? ? ?
}? ? ? ? ? ? ?
@Override? ? ?
public void update() {? ? ? ? ? ?
System.out.println(observerName+"我要更新一下我的狀態(tài)了......");? ? ?
}
}
最后準備一個類測試一下:
public class NotifyMain {? ? ?
public static void main(String[] args) {? ? ? ? ? ?
Subject subject=new ConcreteSubject();? ? ? ? ? ?
Observer observer=new ConcreteObserver("觀察者一號");? ? ? ? ?
? Observer observer2=new ConcreteOberverOther("觀察者二號");? ? ? ? ? ?
subject.attach(observer);? ? ? ? ? ?
subject.attach(observer2);? ? ? ? ? ?
subject.notifyObserver();? ? ?
}
}
將兩個觀察者通過attach添加到觀察者集合中,然后由目標來發(fā)送一條更新的消息。觀察者接收到消息后做出反應。運行結果如下:
三、總結告別
對于上面的分析,我么對觀察者設計模式就有了一個相對比較清晰的認識。但要對它能夠熟練的使用,還需要一定的時間練習。
那么最后我們來總結一下它的優(yōu)缺點,讓我們能更好的使用它。
1.主要優(yōu)點
(1)觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,定義了穩(wěn)定的消息傳遞機制,并抽象了更新接口,使得可以有各種各樣的表示層充當具體的觀察者角色。
(2)觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察者對象只需要維持一個抽象觀察者的集合,無需了解其具體觀察者。
(3)觀察者模式支持廣播通信,觀察目標會向所有已注冊的觀察者發(fā)送通知,降低了一對多系統(tǒng)的設計難度。
(4)觀察者模式滿足開閉原則的要求,增加新的具體觀察者無須修改原有的系統(tǒng)代碼。
2.主要缺點
(1)如果一個觀察目標對象有很多的直接觀察者和間接觀察者,那么所有的觀察者接收到消息會耗費大量的時間。
(2)如果觀察者和被觀察者之間存在循環(huán)依賴,那么觀察目標會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導致系統(tǒng)崩潰。
(3)觀察者模式?jīng)]有相應的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的,而僅僅只是知道目標觀察對象發(fā)生了變化。
設計模式是軟件開發(fā)人員的內(nèi)功,要想成為絕世高手,深厚的內(nèi)功功底必不可少。讓我們一起來修煉這內(nèi)功,讓它成為我們成長路上的墊腳石吧。