主題+訂閱者(觀察者)=觀察者模式
主題「接口」(自定義或者Observable類):remove(Object o);add(Object o);notify();
訂閱者」接口「(自定義或者Observer接口):update();api
觀察者模式:定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它全部的依賴者都會收到通知並自動更新。但注意的是,這種一對多的關係並非絕對的,一個觀察者一樣能夠觀察多個主題。數組
爲了交互對象之間的鬆耦合而努力。鬆耦合的設計之因此能讓咱們創建有彈性的OO系統,可以夠應對變化,是由於對象之間的互相依賴降到了最低。函數
鬆耦合:兩個對象鬆耦合,它們依然能夠交互,可是不清楚彼此的細節。this
觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合:關於觀察者的一切,主題只知道觀察者實現了某個接口(也就是Observer接口)。主題不須要知道觀察者的具體類型是哪一種、作了些什麼或其餘任何細節。spa
實現觀察者模式的方式不止一種,但其中以包含Subject和Observer接口的類設計最爲常見。設計
注意:在例子中還提供了主題是基類觀察者是接口的實例,不推薦的主要緣由是繼承類只能繼承一個這裏繼承了就沒法繼承其餘的。server
觀察者模式有主題推送和觀察者獲取兩種類型:對象
不管是對於主題推送類型仍是觀察者獲取類型,都有一個這樣的功能的實現:訂閱者進行訂閱和取消訂閱。爲何都是要由訂閱者來作,這就是上面的鬆耦合的設計原則,主題不須要知道訂閱者的狀態,只須要知道它實現了訂閱者接口。爲了實現這個功能,首先須要在主題的接口或者基類中中定義相應的public的訂閱和取消訂閱的方法,好比api中Observable類所實現的:繼承
` public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
} ` 接口
`public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
} `
`public synchronized void deleteObservers() {
obs.removeAllElements();
}`
上面的這些方法已經可以讓訂閱者實現自行訂閱的不一樣主題功能(當訂閱者只訂閱一個主題時,訂閱能夠放在構造函數中來實現),除此以外,若是想要訂閱者可以取消訂閱,還須要在訂閱者對象中保留相應主題對象的引用(在api中Observer是一個接口),當要取消訂閱的時候直接經過主題引用調用deleteObserver(this)方法便可。這樣就可以實現訂閱者本身來進行訂閱主題和取消訂閱主題。當一個訂閱者訂閱多個具體主題實現對象的時候,能夠考慮使用容器來進行管理,在取消訂閱的時候也可能夠遍歷容器找到相應的Class類型的主題對象或者找到相同的的主題對象,而後再地調用deleteObserver(this)便可。
api中訂閱者和主題二者接口/基類中的成員,以下圖所示,在主題基類中能夠看到除了咱們上面說的:通知、添加訂閱者、刪除訂閱者這些方法以外,還有一系列關於changed成員變量的方法,爲何要引入這個布爾變量呢?緣由參考Why do I need to call setChanged before I notify the observers?
主題推送在自定義的的時候就是經過notify()在內部遍歷訂閱者數組將所有參數通通發給全部訂閱者,在這裏由於不一樣訂閱者所需的參數不一樣,因此必須將全部參數都傳遞過去。訂閱者中設置了與要接受的某些參數對應類型的成員變量。(實例可參考)
主題推送在基於api實現的時候就是當參數改變時經過調用notifyObservers(Object arg)將參數放在arg裏。在這裏由於不一樣訂閱者所需的參數不一樣,因此必須將全部參數都傳遞過去。而後訂閱者在重寫的update(Observable o, Object arg)裏去取就行,其餘的和自定義基本相同。其中第一個Object o是將主題的引用傳遞過去了,這是爲了讓觀察者可以知道是哪一個主題的通知它的。由於一個觀察者能夠觀察多個主題。
觀察者獲取在自定義的時候就是在主題的具體實現裏添加上全部數據的get方法(由於數據都是私有的),而後在訂閱者在重寫的當中update(Object o);其中o是將主題的引用傳遞過去,經過主題引用來調用get方法獲取所想要的參數(不必定是所有了)。固然也能夠不這樣實現update,直接只是update(),可是須要在訂閱者對象中設置有主題對象的引用,一樣是根據引用調用get方法獲取參數,但這樣的作法缺點在於不利於應對一個訂閱者訂閱多個主題對象的狀況。
觀察者獲取在基於api實現的時候就是當參數改變時經過調用notifyObservers()從而會調用notifyObservers(null),而後調用訂閱者重寫的update(Observable o, null)方法裏來進一步經過主題的引用來調用get方法獲取所想要的參數(不必定是所有了)(實例可參考)。其中第一個Object o是將主題的引用傳遞過去了,這是爲了讓觀察者可以知道是哪一個主題的通知它的。由於一個觀察者能夠觀察多個主題。
基於api實現主題有個顯著的缺點,由於Observable是一個類,而不是接口,若是繼承了Observable類,那就沒法繼承其餘類了。若是必需要繼承其餘類,那仍是須要自行來實現主題接口和主題的具體實現類
任什麼時候候咱們均可以隨時增長觀察者,由於主題惟一依賴的東西是一個實現Observer接口的對象列表,因此咱們能夠隨時增長觀察者。事實上,在運行時咱們能夠用新的觀察者取代舊的觀察者,主題不會受到影響。一樣,也能夠在任什麼時候候刪除觀察者。
有新類型的觀察者出現時,主題的代碼不須要修改。假如咱們有個新的具體類須要當觀察者,咱們不須要爲了兼容新類型而修改主題的代碼,所要作的是在新的類裏實現此觀察者接口,而後註冊爲觀察者便可。
改變主題或觀察者其中一方,並不會影響另外一方。由於二者是鬆耦合的,因此只要他們之間的接口仍被遵照,咱們就能夠自由地改變他們。