觀察者模式(Observer Pattern)
屬於對象行爲型模式
的一種,定義對象之間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆獲得通知並被自動更新。html
<!-- more -->java
觀察者模式
是一種使用率極高的模式,用於創建一種對象與對象之間的依賴關係,一個對象發生改變時將自動通知其餘對象,其餘對象將相應做出反應。在觀察者模式中,發生改變的對象稱爲觀察目標,而被通知的對象稱爲觀察者,一個觀察目標能夠對應多個觀察者,並且這些觀察者之間能夠沒有任何相互聯繫,能夠根據須要增長和刪除觀察者,使得系統更易於擴展。git
觀察者模式的別名包括髮布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。設計模式
前言:觀察者模式有兩種方模型,分別是推模型
和拉模型
數組
UML結構圖安全
1.定義目標對象,它知道觀察它的觀察者,並提供註冊和刪除觀察者的接口微信
class Subject { /** * 用來保存註冊的觀察者對象 */ private List<Observer> observers = new ArrayList<>(); /** * 註冊觀察者對象 * * @param observer 觀察者對象 */ void attach(Observer observer) { observers.add(observer); } /** * 通知全部註冊的觀察者對象 */ void notifyObservers(String newState) { for (Observer observer : observers) { observer.update(newState); } } }
2.具體的目標對象,負責把有關狀態存入到相應的觀察者對象,並在本身狀態發生改變時,通知各個觀察者多線程
class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //狀態發生改變,通知各個觀察者 this.notifyObservers(subjectState); } }
3.建立觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象架構
interface Observer { /** * 更新的接口 * * @param subject 傳入目標對象,好獲取相應的目標對象的狀態 */ void update(String subject); }
4.具體觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致ide
class ConcreteObserver implements Observer { @Override public void update(String newState) { //具體的更新實現 //這裏可能須要更新觀察者的狀態,使其與目標的狀態保持一致 System.out.println("接收到:" + newState); } }
5.建立推模型客戶端,用於測試
public class PushClient { public static void main(String[] args) { //建立主題對象 ConcreteSubject subject = new ConcreteSubject(); //建立觀察者對象 Observer observer = new ConcreteObserver(); //將觀察者對象登記到主題對象上 subject.attach(observer); //改變主題對象的狀態 subject.change("push state"); } }
6.運行結果
接收到:push state
1.定義目標對象,它知道觀察它的觀察者,並提供註冊和刪除觀察者的接口
class Subject { /** * 用來保存註冊的觀察者對象 */ private List<Observer> observers = new ArrayList<>(); /** * 註冊觀察者對象 * * @param observer 觀察者對象 */ public void attach(Observer observer) { observers.add(observer); } /** * 通知全部註冊的觀察者對象 */ public void notifyObservers() { for (Observer observer : observers) { // 注意這句代碼' observer.update(this); } } }
2.具體的目標對象,負責把有關狀態存入到相應的觀察者對象,並在本身狀態發生改變時,通知各個觀察者
class ConcreteSubject extends Subject { /** * 示意,目標對象的狀態 */ private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //狀態發生改變,通知各個觀察者 this.notifyObservers(); } }
3.建立觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象
interface Observer { /** * 更新的接口 * * @param subject 傳入目標對象,好獲取相應的目標對象的狀態 */ void update(Subject subject); }
4.具體觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致
class ConcreteObserver implements Observer { /** * 示意,觀者者的狀態 */ private String observerState; @Override public void update(Subject subject) { //具體的更新實現 //這裏可能須要更新觀察者的狀態,使其與目標的狀態保持一致 observerState = ((ConcreteSubject) subject).getSubjectState(); System.out.println("接收到:" + observerState); } }
5.建立拉模型客戶端,用於測試
public class PullClient { public static void main(String[] args) { //建立主題對象 ConcreteSubject subject = new ConcreteSubject(); //建立觀察者對象 Observer observer = new ConcreteObserver(); //將觀察者對象登記到主題對象上 subject.attach(observer); //改變主題對象的狀態 subject.change("pull state"); } }
6.運行結果
接收到:pull state
上文說過推模型
是假定主題對象知道觀察者須要的數據,這種模型下若是數據發生變動會形成極大的影響;而拉模型是主題對象不知道觀察者具體須要什麼數據,沒有辦法的狀況下,乾脆把自身傳遞給觀察者,讓觀察者本身去按須要取值。因而可知:拉模式的適用範圍更廣;
對於觀察者模式,其實Java已經爲咱們提供了已有的接口和類。對於訂閱者(Subscribe,觀察者)Java爲咱們提供了一個接口。
UML圖
在JAVA語言的 java.util 庫裏面,提供了一個Observable
類以及一個Observer
接口,構成JAVA語言對觀察者模式的支持。
Observer: 只定義了一個 update()
方法,當被觀察者對象的狀態發生變化時,被觀察者對象的 notifyObservers()
方法就會調用這一方法。
public interface Observer { void update(Observable o, Object arg); }
Observable: 充當觀察目標類,在Observable中定義了一個向量Vector來存儲觀察者對象。一個觀察目標類能夠有多個觀察者對象,每一個觀察者對象都是實現Observer接口
的對象。在被觀察者發生變化時,會調用Observable
的notifyObservers()
方法,此方法調用全部的具體觀察者的update()方法, 從而使全部的觀察者都被通知更新本身。
setChanged()
設置一個內部標記變量,表明被觀察者對象的狀態發生了變化。notifyObservers()
調用全部登記過的觀察者對象的update()方法,使這些觀察者對象能夠更新本身。public class Observable { private boolean changed = false; //是否改變狀態,每次都須要設置,表示內容發生變化 private Vector<Observer> obs; //Vector利用同步方法來線程安全,線程安全在多線程狀況下不會形成數據混亂 /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector<>(); } 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); } //通知方法,用於在方法內部循環調用向量中每個觀察者的update()方法。 public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) //狀態值未改變時返回,不通知 return; arrLocal = obs.toArray(); //將Vector轉換成數組 clearChanged(); //重置狀態 } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
1.定義兩個實現了實現java.util.Observer
接口的觀察者
class SubscribeReader implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("開始讀取:" + ((Publish) o).getMessage()); } } class SubscribeWrite implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("開始寫入:" + ((Publish) o).getMessage()); } }
2.建立繼承java.util.Observable
的通知者
class Publish extends Observable { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; //改變通知者的狀態 super.setChanged(); //調用父類Observable方法,通知全部觀察者 super.notifyObservers(); } }
3.建立測試客戶端
public class Client { public static void main(String[] args) { Publish publish = new Publish(); // 遵循FIFO 模型 先進後出 SubscribeWrite write = new SubscribeWrite(); SubscribeReader reader = new SubscribeReader(); publish.addObserver(reader); publish.addObserver(write); publish.setMessage("Hello Battcn"); publish.setMessage("QQ:1837307557"); publish.setMessage("Email:1837307557@qq.com"); } }
4.運行結果
開始寫入:Hello Battcn 開始讀取:Hello Battcn 開始寫入:QQ:1837307557 開始讀取:QQ:1837307557 開始寫入:Email:1837307557@qq.com 開始讀取:Email:1837307557@qq.com
在當前流行的MVC(Model-View-Controller)架構中也應用了觀察者模式,MVC是一種架構模式,它包含三個角色:模型(Model),視圖(View)和控制器(Controller)。其中模型可對應於觀察者模式中的觀察目標,而視圖對應於觀察者,控制器可充當二者之間的中介者。當模型層的數據發生改變時,視圖層將自動改變其顯示內容。
實現的關鍵是要創建觀察者和被觀察者之間的聯繫、好比在被觀察者類中有個集合是用於存放觀察者的、當被檢測的東西發生改變的時候就要通知全部觀察者。在被觀察者中要提供一些對全部觀察者管理的一些方法.目的是添加或者刪除一些觀察者.這樣才能讓被觀察者及時的通知觀察者關係的狀態已經改變、而且調用觀察者通用的方法將變化傳遞過去。
在實現觀察者模式
,若是JDK的Observable類和一個Observer接口能知足需求,直接複用便可,無需本身編寫抽象觀察者、抽象主題類;
可是,java.util.Observable是一個類而不是接口,你必須設計一個類繼承它。若是某個類想同時具備Observable類和另外一個超類的行爲,因爲java不支持多重繼承。因此這個時候就須要本身實現一整套觀察者模式。
優勢
缺點
被觀察者對象
有不少直接和間接的觀察者,那麼將全部的觀察者都通知到會花費不少時間。觀察者模式
是一種使用頻率很是高的設計模式,不管是移動應用、Web應用或者桌面應用,觀察者模式幾乎無處不在,它爲實現對象之間的聯動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景均可以使用觀察者模式。
參考文獻:http://www.cnblogs.com/JsonShare/p/7270546.html
全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter17/battcn-observer
微信公衆號:battcn
(歡迎調戲)